testing init

This commit is contained in:
Ben Wilson 2023-07-14 22:40:36 -04:00
parent f5dad968f6
commit e001383484
12 changed files with 150 additions and 64 deletions

View File

@ -7,6 +7,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AddressController } from './controllers/address/address.controller';
import { ClientController } from './controllers/client/client.controller';
import { BitcoinAddressValidator } from './models/validators/bitcoin-address.validator';
import { AddressSettingsModule } from './ORM/address-settings/address-settings.module';
import { BlocksModule } from './ORM/blocks/blocks.module';
import { ClientStatisticsModule } from './ORM/client-statistics/client-statistics.module';
@ -56,7 +57,8 @@ const ORMModules = [
TelegramService,
BitcoinRpcService,
BlockTemplateService,
NotificationService
NotificationService,
BitcoinAddressValidator
],
})
export class AppModule {

View File

@ -1,4 +1,8 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ClientStatisticsModule } from '../../ORM/client-statistics/client-statistics.module';
import { ClientModule } from '../../ORM/client/client.module';
import { ClientController } from './client.controller';
describe('ClientController', () => {
@ -6,7 +10,20 @@ describe('ClientController', () => {
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
TypeOrmModule.forRoot({
type: 'sqlite',
database: './DB/public-pool.test.sqlite',
synchronize: true,
autoLoadEntities: true,
cache: true,
logging: false
}),
ClientModule,
ClientStatisticsModule
],
controllers: [ClientController],
}).compile();
controller = module.get<ClientController>(ClientController);

View File

@ -1,6 +1,8 @@
import { Controller, Get, NotFoundException, Param } from '@nestjs/common';
import { ClientStatisticsService } from 'src/ORM/client-statistics/client-statistics.service';
import { ClientService } from 'src/ORM/client/client.service';
import { ClientStatisticsService } from '../../ORM/client-statistics/client-statistics.service';
import { ClientService } from '../../ORM/client/client.service';
@Controller('client')
export class ClientController {

View File

@ -1,6 +1,7 @@
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { useContainer } from 'class-validator';
import { AppModule } from './app.module';
@ -23,7 +24,7 @@ async function bootstrap() {
}),
);
app.enableCors();
useContainer(app.select(AppModule), { fallbackOnErrors: true });
await app.listen(process.env.PORT, '0.0.0.0', () => {
console.log(`http listening on port ${process.env.PORT}`);
});

View File

@ -21,7 +21,14 @@ export class MiningJob {
public block: bitcoinjs.Block = new bitcoinjs.Block();
public networkDifficulty: number;
constructor(id: string, payoutInformation: AddressObject[], public blockTemplate: IBlockTemplate, public clean_jobs: boolean) {
constructor(
private network: bitcoinjs.networks.Network,
id: string,
payoutInformation: AddressObject[],
public blockTemplate: IBlockTemplate,
public clean_jobs: boolean) {
console.log(JSON.stringify(blockTemplate))
this.jobId = id;
this.block.prevHash = this.convertToLittleEndian(blockTemplate.previousblockhash);
@ -155,19 +162,19 @@ export class MiningJob {
const addressInfo = getAddressInfo(address);
switch (addressInfo.type) {
case AddressType.p2wpkh: {
return bitcoinjs.payments.p2wpkh({ address }).output;
return bitcoinjs.payments.p2wpkh({ address, network: this.network }).output;
}
case AddressType.p2pkh: {
return bitcoinjs.payments.p2pkh({ address }).output;
return bitcoinjs.payments.p2pkh({ address, network: this.network }).output;
}
case AddressType.p2sh: {
return bitcoinjs.payments.p2sh({ address }).output;
return bitcoinjs.payments.p2sh({ address, network: this.network }).output;
}
case AddressType.p2tr: {
return bitcoinjs.payments.p2tr({ address }).output;
return bitcoinjs.payments.p2tr({ address, network: this.network }).output;
}
case AddressType.p2wsh: {
return bitcoinjs.payments.p2wsh({ address }).output;
return bitcoinjs.payments.p2wsh({ address, network: this.network }).output;
}
default: {
return Buffer.alloc(0);

View File

@ -0,0 +1,54 @@
import { ConfigService } from '@nestjs/config';
import { PromiseSocket } from 'promise-socket';
import { BlocksService } from '../ORM/blocks/blocks.service';
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
import { ClientService } from '../ORM/client/client.service';
import { BitcoinRpcService } from '../services/bitcoin-rpc.service';
import { BlockTemplateService } from '../services/block-template.service';
import { NotificationService } from '../services/notification.service';
import { StratumV1JobsService } from '../services/stratum-v1-jobs.service';
import { StratumV1Client } from './StratumV1Client';
describe('StratumV1Client', () => {
let promiseSocket: PromiseSocket<any> = new PromiseSocket();
let stratumV1JobsService: StratumV1JobsService;
let bitcoinRpcService: BitcoinRpcService;
let blockTemplateService: BlockTemplateService;
let clientService: ClientService;
let clientStatisticsService: ClientStatisticsService;
let notificationService: NotificationService;
let blocksService: BlocksService;
let configService: ConfigService;
let client: StratumV1Client;
beforeEach(async () => {
jest.spyOn(promiseSocket.socket, 'on').mockImplementation();
client = new StratumV1Client(
promiseSocket,
stratumV1JobsService,
blockTemplateService,
bitcoinRpcService,
clientService,
clientStatisticsService,
notificationService,
blocksService,
configService
);
});
it('should parse message', () => {
expect(promiseSocket.socket.on).toHaveBeenCalled();
});
});

View File

@ -1,3 +1,4 @@
import { ConfigService } from '@nestjs/config';
import Big from 'big.js';
import * as bitcoinjs from 'bitcoinjs-lib';
import { plainToInstance } from 'class-transformer';
@ -6,7 +7,6 @@ import * as crypto from 'crypto';
import { Socket } from 'net';
import PromiseSocket from 'promise-socket';
import { combineLatest, firstValueFrom, interval, startWith, takeUntil } from 'rxjs';
import { NotificationService } from 'src/services/notification.service';
import { BlocksService } from '../ORM/blocks/blocks.service';
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
@ -14,6 +14,7 @@ import { ClientEntity } from '../ORM/client/client.entity';
import { ClientService } from '../ORM/client/client.service';
import { BitcoinRpcService } from '../services/bitcoin-rpc.service';
import { BlockTemplateService } from '../services/block-template.service';
import { NotificationService } from '../services/notification.service';
import { StratumV1JobsService } from '../services/stratum-v1-jobs.service';
import { EasyUnsubscribe } from '../utils/EasyUnsubscribe';
import { IBlockTemplate } from './bitcoin-rpc/IBlockTemplate';
@ -29,6 +30,7 @@ import { SubscriptionMessage } from './stratum-messages/SubscriptionMessage';
import { SuggestDifficulty } from './stratum-messages/SuggestDifficultyMessage';
import { StratumV1ClientStatistics } from './StratumV1ClientStatistics';
export class StratumV1Client extends EasyUnsubscribe {
private clientSubscription: SubscriptionMessage;
@ -52,7 +54,8 @@ export class StratumV1Client extends EasyUnsubscribe {
private readonly clientService: ClientService,
private readonly clientStatisticsService: ClientStatisticsService,
private readonly notificationService: NotificationService,
private readonly blocksService: BlocksService
private readonly blocksService: BlocksService,
private readonly configService: ConfigService
) {
super();
@ -67,7 +70,6 @@ export class StratumV1Client extends EasyUnsubscribe {
.filter(m => m.length > 0)
.forEach(m => this.handleMessage(m))
});
}
private getRandomHexString() {
@ -91,6 +93,7 @@ export class StratumV1Client extends EasyUnsubscribe {
return;
}
switch (parsedMessage.method) {
case eRequestMethod.SUBSCRIBE: {
const subscriptionMessage = plainToInstance(
@ -312,12 +315,18 @@ export class StratumV1Client extends EasyUnsubscribe {
} else {
payoutInformation = [
{ address: 'bc1q99n3pu025yyu0jlywpmwzalyhm36tg5u37w20d', percent: 1.5 },
{ address: this.configService.get('DEV_FEE_ADDRESS'), percent: 1.5 },
{ address: this.clientAuthorization.address, percent: 98.5 }
];
}
const job = new MiningJob(this.stratumV1JobsService.getNextId(), payoutInformation, blockTemplate, clearJobs);
const job = new MiningJob(
this.configService.get('NETWORK') === 'mainnet' ? bitcoinjs.networks.bitcoin : bitcoinjs.networks.testnet,
this.stratumV1JobsService.getNextId(),
payoutInformation,
blockTemplate,
clearJobs
);
this.stratumV1JobsService.addJob(job, clearJobs);
@ -427,7 +436,7 @@ export class StratumV1Client extends EasyUnsubscribe {
}
}
public calculateDifficulty(header: Buffer): { submissionDifficulty: number, submissionHash: string } {
private calculateDifficulty(header: Buffer): { submissionDifficulty: number, submissionHash: string } {
const hashResult = bitcoinjs.crypto.hash256(header);

View File

@ -24,7 +24,7 @@ export class AuthorizationMessage extends StratumBaseMessage {
@IsString()
@MaxLength(64)
@Transform(({ value, key, obj, type }) => {
return obj.params[0].split('.')[1];
return obj.params[0].split('.')[1] == null ? 'worker' : obj.params[0].split('.')[1];
})
public worker: string;

View File

@ -1,3 +1,7 @@
import { plainToInstance } from 'class-transformer';
import { MiningSubmitMessage } from './MiningSubmitMessage';
describe('MiningSubmitMessage', () => {
@ -7,48 +11,25 @@ describe('MiningSubmitMessage', () => {
});
// describe('test nonce', () => {
describe('test message parsing', () => {
// const value = new MiningSubmitMessage().testNonceValue({
// version: 0x20000004,
// prevhash: "0c859545a3498373a57452fac22eb7113df2a465000543520000000000000000",
// merkleRoot: "5bdc1968499c3393873edf8e07a1c3a50a97fc3a9d1a376bbf77087dd63778eb",
// ntime: 0x647025b5,
// nbits: 0x1705ae3a,
// } as unknown as MiningJob, 167943889);
const MINING_SUBMIT_MESSAGE = ' {"id": 5, "method": "mining.submit", "params": ["tb1qumezefzdeqqwn5zfvgdrhxjzc5ylr39uhuxcz4.bitaxe3", "1", "99020000", "64b1f10f", "2402812d", "00006000"]}'
// it('should be correct difficulty', () => {
// expect(value).toEqual(683);
// });
// });
const message = plainToInstance(
MiningSubmitMessage,
JSON.parse(MINING_SUBMIT_MESSAGE),
);
// describe('test empty version', () => {
it('should parse message', () => {
expect(message.id).toEqual(5);
expect(message.userId).toEqual('tb1qumezefzdeqqwn5zfvgdrhxjzc5ylr39uhuxcz4.bitaxe3');
expect(message.jobId).toEqual('1');
expect(message.extraNonce2).toEqual('99020000');
expect(message.ntime).toEqual('64b1f10f');
expect(message.nonce).toEqual('2402812d');
expect(message.versionMask).toEqual('00006000');
});
});
// const value = new MiningSubmitMessage().testNonceValue({
// version: 0x20000004,
// prevhash: "0c859545a3498373a57452fac22eb7113df2a465000543520000000000000000",
// merkleRoot: "5bdc1968499c3393873edf8e07a1c3a50a97fc3a9d1a376bbf77087dd63778eb",
// ntime: 0x647025b5,
// nbits: 0x1705ae3a,
// } as unknown as MiningJob, 167943889, parseInt('00000000', 16));
// it('should be correct difficulty', () => {
// expect(value).toEqual(683);
// });
// });
// describe('test high value nonce', () => {
// const value = new MiningSubmitMessage().testNonceValue({
// version: 0x20a00000,
// prevhash: "00000000000000000002a7b66a599d17893cb312a8ee7bc15e4015ff52774f00",
// merkleRoot: "210bbf45c85165aab889691056cfebbfd763e11b2623a261fb6135b6bab66ce3",
// ntime: 1686839100,
// target: 52350439455487,
// } as MiningJob, 0x05d69c40,);
// it('should be correct difficulty', () => {
// expect(value).toEqual(683);
// });
// });
});

View File

@ -1,11 +1,19 @@
import { Network, validate } from 'bitcoin-address-validation';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { validate } from 'bitcoin-address-validation';
import { registerDecorator, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
@ValidatorConstraint({ name: 'bitcoinAddress', async: false })
export class BitcoinAddress implements ValidatorConstraintInterface {
@Injectable()
export class BitcoinAddressValidator implements ValidatorConstraintInterface {
constructor(
private configService: ConfigService
) { }
validate(value: string): boolean {
// Implement your custom validation logic here
return validate(value, Network.mainnet);
return validate(value, this.configService.get('NETWORK'));
}
defaultMessage(): string {
@ -21,7 +29,7 @@ export function IsBitcoinAddress(validationOptions?: ValidationOptions) {
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: BitcoinAddress,
validator: BitcoinAddressValidator,
});
};
}

View File

@ -1,4 +1,5 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Server, Socket } from 'net';
import { PromiseSocket } from 'promise-socket';
@ -21,7 +22,8 @@ export class StratumV1Service implements OnModuleInit {
private readonly clientService: ClientService,
private readonly clientStatisticsService: ClientStatisticsService,
private readonly notificationService: NotificationService,
private readonly blocksService: BlocksService
private readonly blocksService: BlocksService,
private readonly configService: ConfigService
) {
}
@ -47,7 +49,8 @@ export class StratumV1Service implements OnModuleInit {
this.clientService,
this.clientStatisticsService,
this.notificationService,
this.blocksService
this.blocksService,
this.configService
);

View File

@ -3,7 +3,9 @@ import { ConfigService } from '@nestjs/config';
import { validate } from 'bitcoin-address-validation';
import { Block } from 'bitcoinjs-lib';
import * as TelegramBot from 'node-telegram-bot-api';
import { TelegramSubscriptionsService } from 'src/ORM/telegram-subscriptions/telegram-subscriptions.service';
import { TelegramSubscriptionsService } from '../ORM/telegram-subscriptions/telegram-subscriptions.service';
@Injectable()
export class TelegramService implements OnModuleInit {