dependency updates

This commit is contained in:
Ben
2026-05-06 19:59:58 -04:00
parent 93f09ae889
commit 5f66d082ea
13 changed files with 7710 additions and 8293 deletions
@@ -67,10 +67,14 @@ export class AddressSettingsService {
// }
public async resetBestDifficultyAndShares() {
return await this.addressSettingsRepository.update({}, {
shares: 0,
bestDifficulty: 0
});
return await this.addressSettingsRepository
.createQueryBuilder()
.update(AddressSettingsEntity)
.set({
shares: 0,
bestDifficulty: 0
})
.execute();
}
public async getHighScores() {
@@ -259,6 +259,6 @@ export class ClientStatisticsService {
}
public async deleteAll() {
return await this.clientStatisticsRepository.delete({})
return await this.clientStatisticsRepository.clear()
}
}
}
+4 -1
View File
@@ -184,7 +184,10 @@ export class ClientService {
}
public async deleteAll() {
return await this.clientRepository.softDelete({})
return await this.clientRepository
.createQueryBuilder()
.softDelete()
.execute();
}
// public async getUserAgents() {
+1 -12
View File
@@ -1,5 +1,4 @@
import { Controller, Patch } from '@nestjs/common';
import * as bitcoinMessage from 'bitcoinjs-message';
@Controller('address')
@@ -7,16 +6,6 @@ export class AddressController {
@Patch('settings')
async settings() {
const publicKey = '...'; // Public key corresponding to the private key used for signing
const message = '...'; // The message that was signed
const signature = '...'; // The signature of the message
const isValid: boolean = bitcoinMessage.verify(message, publicKey, signature);
if (isValid) {
console.log('Signature is valid!');
} else {
console.log('Signature is not valid!');
}
return;
}
}
@@ -13,7 +13,7 @@ describe('ClientController', () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
type: 'better-sqlite3',
database: ':memory:',
synchronize: true,
autoLoadEntities: true,
+4 -4
View File
@@ -66,7 +66,7 @@ describe('StratumV1Client', () => {
moduleRef = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
type: 'better-sqlite3',
database: ':memory:',
synchronize: true,
autoLoadEntities: true,
@@ -112,9 +112,9 @@ describe('StratumV1Client', () => {
const dataSource = moduleRef.get<DataSource>(DataSource);
await dataSource.getRepository(ClientStatisticsEntity).delete({});
await dataSource.getRepository(ClientEntity).delete({});
await dataSource.getRepository(BlocksEntity).delete({});
await dataSource.getRepository(ClientStatisticsEntity).clear();
await dataSource.getRepository(ClientEntity).clear();
await dataSource.getRepository(BlocksEntity).clear();
clientStatisticsService = moduleRef.get<ClientStatisticsService>(ClientStatisticsService);
+1 -1
View File
@@ -53,7 +53,7 @@ export class StratumV1Client {
private clientAuthorization: AuthorizationMessage;
private clientSuggestedDifficulty: SuggestDifficulty;
private stratumSubscription: Subscription;
private backgroundWork: NodeJS.Timer[] = [];
private backgroundWork: NodeJS.Timeout[] = [];
private statistics: StratumV1ClientStatistics;
private stratumInitialized = false;
+42 -11
View File
@@ -1,6 +1,6 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { RPCClient } from 'rpc-bitcoin';
import axios, { AxiosInstance } from 'axios';
import { asyncScheduler, BehaviorSubject, delay, filter, from, interval, scheduled, shareReplay, startWith, Subject, switchMap } from 'rxjs';
import { RpcBlockService } from '../ORM/rpc-block/rpc-block.service';
import * as zmq from 'zeromq';
@@ -14,10 +14,11 @@ import * as fs from 'node:fs';
export class BitcoinRpcService implements OnModuleInit {
private client: RPCClient;
private client: AxiosInstance;
private _newBlockTemplate$: BehaviorSubject<IBlockTemplate> = new BehaviorSubject(undefined);
private pubsubInstance: PGPubsub;
private resetTemplateInterval$ = new Subject<void>();
private rpcRequestId = 0;
public miningInfo: IMiningInfo;
public newBlockTemplate$ = this._newBlockTemplate$.pipe(filter(block => block != null), shareReplay({ refCount: true, bufferSize: 1 }));
@@ -47,9 +48,17 @@ export class BitcoinRpcService implements OnModuleInit {
pass = cookiePass;
}
this.client = new RPCClient({ url, port, timeout, user, pass });
const baseURL = this.buildRpcUrl(url, port);
this.client = axios.create({
baseURL,
timeout,
auth: {
username: user,
password: pass
}
});
this.client.getrpcinfo().then((res) => {
this.callRpc('getrpcinfo').then((res) => {
console.log('Bitcoin RPC connected');
}, () => {
console.error('Could not reach RPC host');
@@ -117,13 +126,13 @@ export class BitcoinRpcService implements OnModuleInit {
let blockTemplate: IBlockTemplate;
while (blockTemplate == null) {
blockTemplate = await this.client.getblocktemplate({
template_request: {
blockTemplate = await this.callRpc<IBlockTemplate>('getblocktemplate', [
{
rules: ['segwit'],
mode: 'template',
capabilities: ['serverlist', 'proposal']
}
});
]);
}
try {
@@ -139,7 +148,7 @@ export class BitcoinRpcService implements OnModuleInit {
public async getMiningInfo(): Promise<IMiningInfo> {
try {
return await this.client.getmininginfo();
return await this.callRpc<IMiningInfo>('getmininginfo');
} catch (e) {
console.error('Error getmininginfo', e.message);
return null;
@@ -150,9 +159,7 @@ export class BitcoinRpcService implements OnModuleInit {
public async SUBMIT_BLOCK(hexdata: string): Promise<string> {
let response: string = 'unknown';
try {
response = await this.client.submitblock({
hexdata
});
response = await this.callRpc<string>('submitblock', [hexdata]);
if (response == null) {
response = 'SUCCESS!';
}
@@ -166,4 +173,28 @@ export class BitcoinRpcService implements OnModuleInit {
return response;
}
private async callRpc<T>(method: string, params: unknown[] = []): Promise<T> {
const response = await this.client.post('', {
jsonrpc: '1.0',
id: ++this.rpcRequestId,
method,
params
});
if (response.data.error != null) {
throw response.data.error;
}
return response.data.result;
}
private buildRpcUrl(url: string, port: number): string {
const normalizedUrl = /^https?:\/\//i.test(url) ? url : `http://${url}`;
const rpcUrl = new URL(normalizedUrl);
if (Number.isFinite(port) && port > 0) {
rpcUrl.port = port.toString();
}
return rpcUrl.toString();
}
}
+64 -23
View File
@@ -1,8 +1,8 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios, { AxiosInstance } from 'axios';
import { validate } from 'bitcoin-address-validation';
import { Block } from 'bitcoinjs-lib';
import * as TelegramBot from 'node-telegram-bot-api';
import { TelegramSubscriptionsService } from '../ORM/telegram-subscriptions/telegram-subscriptions.service';
@@ -10,7 +10,9 @@ import { TelegramSubscriptionsService } from '../ORM/telegram-subscriptions/tele
@Injectable()
export class TelegramService implements OnModuleInit {
private bot: TelegramBot;
private bot: AxiosInstance;
private updateOffset = 0;
private pollingTimer: NodeJS.Timeout;
constructor(
private readonly configService: ConfigService,
@@ -20,7 +22,10 @@ export class TelegramService implements OnModuleInit {
if (token == null || token.length < 1) {
return;
}
this.bot = new TelegramBot(token, { polling: true });
this.bot = axios.create({
baseURL: `https://api.telegram.org/bot${token}/`,
timeout: 10000
});
console.log('Telegram bot init');
@@ -32,23 +37,10 @@ export class TelegramService implements OnModuleInit {
return;
}
this.bot.onText(/\/subscribe/, async (msg) => {
const address = msg.text.split('/subscribe ')[1];
if (validate(address) == false) {
this.bot.sendMessage(msg.chat.id, "Invalid address.");
return;
}
await this.telegramSubscriptionsService.saveSubscription(msg.chat.id, address);
this.bot.sendMessage(msg.chat.id, "Subscribed!");
});
this.bot.onText(/\/start/, (msg) => {
this.bot.sendMessage(msg.chat.id, "Welcome to the public-pool bot. /subscribe <address> to get notified.");
});
this.bot.on('message', (msg) => {
console.log(msg);
});
await this.pollUpdates();
this.pollingTimer = setInterval(async () => {
await this.pollUpdates();
}, 2000);
}
public async notifySubscribersBlockFound(address: string, height: number, block: Block, message: string) {
@@ -57,8 +49,57 @@ export class TelegramService implements OnModuleInit {
}
const subscribers = await this.telegramSubscriptionsService.getSubscriptions(address);
subscribers.forEach(subscriber => {
this.bot.sendMessage(subscriber.telegramChatId, `Block Found! Result: ${message}, Height: ${height}`);
await Promise.all(subscribers.map(subscriber => {
return this.sendMessage(subscriber.telegramChatId, `Block Found! Result: ${message}, Height: ${height}`);
}));
}
private async pollUpdates() {
try {
const response = await this.bot.get('getUpdates', {
params: {
offset: this.updateOffset,
timeout: 0
}
});
for (const update of response.data.result ?? []) {
this.updateOffset = update.update_id + 1;
await this.handleMessage(update.message);
}
} catch (e) {
console.error('Telegram polling failed', e.message);
}
}
private async handleMessage(msg: any) {
if (msg?.text == null) {
return;
}
if (msg.text.startsWith('/subscribe')) {
const address = msg.text.split('/subscribe ')[1];
if (validate(address) == false) {
await this.sendMessage(msg.chat.id, 'Invalid address.');
return;
}
await this.telegramSubscriptionsService.saveSubscription(msg.chat.id, address);
await this.sendMessage(msg.chat.id, 'Subscribed!');
return;
}
if (msg.text.startsWith('/start')) {
await this.sendMessage(msg.chat.id, 'Welcome to the public-pool bot. /subscribe <address> to get notified.');
return;
}
console.log(msg);
}
private async sendMessage(chatId: number | string, text: string) {
await this.bot.post('sendMessage', {
chat_id: chatId,
text
});
}
}
}