diff --git a/.env.example b/.env.example index c5950e4..b3ceea5 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,8 @@ BITCOIN_RPC_PASSWORD= BITCOIN_RPC_PORT=8332 BITCOIN_RPC_TIMEOUT=10000 -#statum port -PORT=3333 +API_PORT=3334 +STRATUM_PORT=3333 #optional telegram bot #TELEGRAM_BOT_TOKEN= @@ -20,4 +20,11 @@ DEV_FEE_ADDRESS= # mainnet | testnet NETWORK=mainnet -API_SECURE=false \ No newline at end of file +API_SECURE=false + +ENABLE_SOLO=false +ENABLE_PROXY=true + +BRAIINS_ACCESS_TOKEN= + +PROXY_PORT=3333 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 96065aa..ac83d76 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "cSpell.words": [ "bitcoinjs", + "braiins", "coinb", "coinbasevalue", "Fastify", diff --git a/package-lock.json b/package-lock.json index 641008f..8e02ad5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { + "@nestjs/axios": "^3.0.0", "@nestjs/cache-manager": "^2.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.2", @@ -16,6 +17,7 @@ "@nestjs/platform-fastify": "^9.4.2", "@nestjs/schedule": "^3.0.1", "@nestjs/typeorm": "^10.0.0", + "axios": "^1.4.0", "big.js": "^6.2.1", "bitcoin-address-validation": "^2.2.1", "bitcoinjs-lib": "^6.1.3", @@ -1657,6 +1659,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@nestjs/axios": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.0.tgz", + "integrity": "sha512-ULdH03jDWkS5dy9X69XbUVbhC+0pVnrRcj7bIK/ytTZ76w7CgvTZDJqsIyisg3kNOiljRW/4NIjSf3j6YGvl+g==", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0", + "axios": "^1.3.1", + "reflect-metadata": "^0.1.12", + "rxjs": "^6.0.0 || ^7.0.0" + } + }, "node_modules/@nestjs/cache-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.0.1.tgz", @@ -3229,6 +3242,29 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", @@ -5388,6 +5424,25 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -8974,6 +9029,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -12546,6 +12606,12 @@ } } }, + "@nestjs/axios": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-3.0.0.tgz", + "integrity": "sha512-ULdH03jDWkS5dy9X69XbUVbhC+0pVnrRcj7bIK/ytTZ76w7CgvTZDJqsIyisg3kNOiljRW/4NIjSf3j6YGvl+g==", + "requires": {} + }, "@nestjs/cache-manager": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@nestjs/cache-manager/-/cache-manager-2.0.1.tgz", @@ -13764,6 +13830,28 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "babel-jest": { "version": "29.5.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", @@ -15427,6 +15515,11 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -18132,6 +18225,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index eb0625c..a29b0d6 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test:e2e": "jest --config ./test/jest-e2e.json" }, "dependencies": { + "@nestjs/axios": "^3.0.0", "@nestjs/cache-manager": "^2.0.1", "@nestjs/common": "^9.0.0", "@nestjs/config": "^2.3.2", @@ -27,6 +28,7 @@ "@nestjs/platform-fastify": "^9.4.2", "@nestjs/schedule": "^3.0.1", "@nestjs/typeorm": "^10.0.0", + "axios": "^1.4.0", "big.js": "^6.2.1", "bitcoin-address-validation": "^2.2.1", "bitcoinjs-lib": "^6.1.3", diff --git a/src/app.module.ts b/src/app.module.ts index 8154a0d..c19d0dc 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,3 +1,4 @@ +import { HttpModule } from '@nestjs/axios'; import { CacheModule } from '@nestjs/cache-manager'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; @@ -15,8 +16,11 @@ import { ClientModule } from './ORM/client/client.module'; import { TelegramSubscriptionsModule } from './ORM/telegram-subscriptions/telegram-subscriptions.module'; import { AppService } from './services/app.service'; import { BitcoinRpcService } from './services/bitcoin-rpc.service'; +import { BraiinsService } from './services/braiins.service'; +import { BTCPayService } from './services/btc-pay.service'; import { DiscordService } from './services/discord.service'; import { NotificationService } from './services/notification.service'; +import { ProxyService } from './services/proxy.service'; import { StratumV1JobsService } from './services/stratum-v1-jobs.service'; import { StratumV1Service } from './services/stratum-v1.service'; import { TelegramService } from './services/telegram.service'; @@ -44,7 +48,7 @@ const ORMModules = [ }), CacheModule.register(), ScheduleModule.forRoot(), - + HttpModule, ...ORMModules ], controllers: [ @@ -60,7 +64,10 @@ const ORMModules = [ BitcoinRpcService, NotificationService, BitcoinAddressValidator, - StratumV1JobsService + StratumV1JobsService, + ProxyService, + BTCPayService, + BraiinsService ], }) export class AppModule { diff --git a/src/main.ts b/src/main.ts index bfcea7b..2540678 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,7 +10,7 @@ import { AppModule } from './app.module'; async function bootstrap() { - if (process.env.PORT == null) { + if (process.env.API_PORT == null) { console.error('It appears your environment is not configured, create and populate an .env file.'); return; } @@ -45,7 +45,7 @@ async function bootstrap() { //Taproot bitcoinjs.initEccLib(ecc); - await app.listen(process.env.PORT, '0.0.0.0', (err, address) => { + await app.listen(process.env.API_PORT, '0.0.0.0', (err, address) => { console.log(`API listening on ${address}`); }); diff --git a/src/models/MiningJob.ts b/src/models/MiningJob.ts index 35ae77e..8ae18d5 100644 --- a/src/models/MiningJob.ts +++ b/src/models/MiningJob.ts @@ -3,6 +3,7 @@ import * as bitcoinjs from 'bitcoinjs-lib'; import { IJobTemplate } from '../services/stratum-v1-jobs.service'; import { eResponseMethod } from './enums/eResponseMethod'; +import { IMiningNotify } from './stratum-messages/IMiningNotify'; interface AddressObject { @@ -161,7 +162,7 @@ export class MiningJob { public response(jobTemplate: IJobTemplate): string { - const job = { + const job: IMiningNotify = { id: null, method: eResponseMethod.MINING_NOTIFY, params: [ diff --git a/src/models/ProxyClient.ts b/src/models/ProxyClient.ts new file mode 100644 index 0000000..0bae955 --- /dev/null +++ b/src/models/ProxyClient.ts @@ -0,0 +1,235 @@ +import Big from 'big.js'; +import { Block } from 'bitcoinjs-lib'; +import * as bitcoinjs from 'bitcoinjs-lib'; +import { plainToInstance } from 'class-transformer'; +import { validate, ValidatorOptions } from 'class-validator'; +import { Socket } from 'net'; + +import { eRequestMethod } from './enums/eRequestMethod'; +import { eResponseMethod } from './enums/eResponseMethod'; +import { IMiningNotify } from './stratum-messages/IMiningNotify'; +import { MiningSubmitMessage } from './stratum-messages/MiningSubmitMessage'; + +export class ProxyClient { + + + public braiinsSocket: Socket; + public serverDifficulty: number; + public jobs: { [jobId: string]: IMiningNotify } = {}; + private extraNonce: string; + + constructor(public socket: Socket) { + this.braiinsSocket = new Socket(); + this.braiinsSocket.connect(3333, 'us-east.stratum.braiins.com', () => { + console.log('Connected to braiins'); + }); + + this.braiinsSocket.on('data', (data) => { + data.toString() + .split('\n') + .filter(m => m.length > 0) + .forEach(async (m) => { + try { + await this.toClient(m); + } catch (e) { + await socket.end(); + console.error(e); + } + }) + + }); + + this.braiinsSocket.on('close', () => { + console.log('Braiins closed connection'); + socket.end(); + }); + + + socket.on('error', async (error: Error) => { }); + + socket.on('data', (data: Buffer) => { + data.toString() + .split('\n') + .filter(m => m.length > 0) + .forEach(async (m) => { + try { + await this.toServer(m); + } catch (e) { + await socket.end(); + console.error(e); + } + }) + }); + socket.on('close', () => { + console.log('Client closed connection'); + this.braiinsSocket.end(); + }) + } + + private async toClient(message: string) { + let parsedMessage: IMiningNotify | any; + try { + parsedMessage = JSON.parse(message); + } catch (e) { + //console.log("Invalid JSON"); + await this.socket.end(); + return; + } + + if (parsedMessage.method == null && Array.isArray(parsedMessage.result)) { + this.extraNonce = parsedMessage.result[1]; + } + + switch (parsedMessage.method) { + case eResponseMethod.SET_DIFFICULTY: { + this.serverDifficulty = parsedMessage.params[0]; + parsedMessage.params[0] = 512; + message = JSON.stringify(parsedMessage); + break; + } + case eResponseMethod.MINING_NOTIFY: { + // clear jobs + if ((parsedMessage as IMiningNotify).params[8]) { + this.jobs = {}; + } + this.jobs[parsedMessage.params[0]] = parsedMessage; + break; + } + } + console.log('Server:'); + console.log(message); + this.socket.write(message + `\n`); + } + private async toServer(message: string) { + + let parsedMessage = null; + try { + parsedMessage = JSON.parse(message); + } catch (e) { + //console.log("Invalid JSON"); + await this.socket.end(); + return; + } + + switch (parsedMessage.method) { + + case eRequestMethod.AUTHORIZE: { + parsedMessage.params[0] = 'battlechicken'; + message = JSON.stringify(parsedMessage); + break; + } + case eRequestMethod.SUBMIT: { + parsedMessage.params[0] = 'battlechicken'; + + message = JSON.stringify(parsedMessage); + + const miningSubmitMessage = plainToInstance( + MiningSubmitMessage, + parsedMessage, + ); + + const validatorOptions: ValidatorOptions = { + whitelist: true, + forbidNonWhitelisted: true, + }; + + const errors = await validate(miningSubmitMessage, validatorOptions); + + if (errors.length === 0) { + this.checkSubmission(miningSubmitMessage); + } + else { + console.log('parsing error') + } + break; + } + } + console.log('Client:'); + console.log(message); + this.braiinsSocket.write(message + '\n'); + + } + + private checkSubmission(submission: MiningSubmitMessage) { + console.log('Submission:'); + console.log(submission); + + const job = this.jobs[submission.jobId]; + if (job == null) { + console.log('Job not found'); + return; + } + + const versionMask = parseInt(submission.versionMask, 16); + + const block = new Block(); + + block.version = parseInt(job.params[5], 16); + if (submission.versionMask !== undefined && versionMask != 0) { + block.version = (block.version ^ versionMask); + } + + const prevHash = this.swapEndianWords(job.params[1]); + block.prevHash = Buffer.from(prevHash, 'hex'); + const coinbase = Buffer.from(`${job.params[2]}${this.extraNonce}${submission.extraNonce2}${job.params[3]}`, 'hex'); + const coinbaseHash = bitcoinjs.crypto.hash256(coinbase); + block.merkleRoot = this.calculateMerkleRootHash(coinbaseHash, job.params[4]); + block.timestamp = parseInt(submission.ntime, 16); + block.bits = parseInt(job.params[6], 16); + block.nonce = parseInt(submission.nonce, 16); + + const header = block.toBuffer(true); + + const diff = this.calculateDifficulty(header); + + console.log(`DIFFICULTY: ${diff.submissionDifficulty}`) + } + + + private calculateMerkleRootHash(newRoot: Buffer, merkleBranches: string[]): Buffer { + + const bothMerkles = Buffer.alloc(64); + + bothMerkles.set(newRoot); + + for (let i = 0; i < merkleBranches.length; i++) { + bothMerkles.set(Buffer.from(merkleBranches[i], 'hex'), 32); + newRoot = bitcoinjs.crypto.hash256(bothMerkles); + bothMerkles.set(newRoot); + } + + return bothMerkles.subarray(0, 32) + } + + private calculateDifficulty(header: Buffer): { submissionDifficulty: number, submissionHash: string } { + + const hashResult = bitcoinjs.crypto.hash256(header); + + let s64 = this.le256todouble(hashResult); + + const truediffone = Big('26959535291011309493156476344723991336010898738574164086137773096960'); + const difficulty = truediffone.div(s64.toString()); + return { submissionDifficulty: difficulty.toNumber(), submissionHash: hashResult.toString('hex') }; + } + + private le256todouble(target: Buffer): bigint { + + const number = target.reduceRight((acc, byte) => { + // Shift the number 8 bits to the left and OR with the current byte + return (acc << BigInt(8)) | BigInt(byte); + }, BigInt(0)); + + return number; + } + + private swapEndianWords(str: string) { + const hexGroups = str.match(/.{1,8}/g); + // Reverse each group and concatenate them + const reversedHexString = hexGroups.reduce((pre, cur, indx, arr) => { + const reversed = cur.match(/.{2}/g).reverse(); + return `${pre}${reversed.join('')}`; + }, ''); + return reversedHexString; + } + +} \ No newline at end of file diff --git a/src/models/stratum-messages/IMiningNotify.ts b/src/models/stratum-messages/IMiningNotify.ts new file mode 100644 index 0000000..8fd81f3 --- /dev/null +++ b/src/models/stratum-messages/IMiningNotify.ts @@ -0,0 +1,17 @@ +import { eResponseMethod } from '../enums/eResponseMethod'; + +export interface IMiningNotify { + id: null, + method: eResponseMethod.MINING_NOTIFY, + params: [ + string, // jobId 0 + string, // prevHash 1 + string, // coinbase part1 2 + string, // coinbase part2 3 + string[], // merkle_branch 4 + string, // version 5 + string, // bits 6 + string, // timestamp 7 + boolean // clear jobs 8 + ] +} \ No newline at end of file diff --git a/src/services/app.service.ts b/src/services/app.service.ts index 9af2afe..a14b8f5 100644 --- a/src/services/app.service.ts +++ b/src/services/app.service.ts @@ -28,7 +28,7 @@ export class AppService implements OnModuleInit { @Cron(CronExpression.EVERY_HOUR) private async deleteOldStatistics() { - if (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0') { + if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) { const deletedStatistics = await this.clientStatisticsService.deleteOldStatistics(); console.log(`Deleted ${deletedStatistics.affected} old statistics`); const deletedClients = await this.clientService.deleteOldClients(); @@ -38,7 +38,7 @@ export class AppService implements OnModuleInit { @Cron(CronExpression.EVERY_5_MINUTES) private async killDeadClients() { - if (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0') { + if (process.env.ENABLE_SOLO == 'true' && (process.env.NODE_APP_INSTANCE == null || process.env.NODE_APP_INSTANCE == '0')) { await this.clientService.killDeadClients(); } } diff --git a/src/services/braiins.service.ts b/src/services/braiins.service.ts new file mode 100644 index 0000000..556402e --- /dev/null +++ b/src/services/braiins.service.ts @@ -0,0 +1,36 @@ +import { HttpService } from '@nestjs/axios'; +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { map } from 'rxjs'; + +@Injectable() +export class BraiinsService implements OnModuleInit { + + constructor(private readonly httpService: HttpService) { + + } + + onModuleInit() { + + // this.getUserProfile().subscribe((res) => { + // console.log(`Braiins:`) + // console.log(res); + // }) + // this.getBlockRewards(new Date(0), new Date()).subscribe(res => { + // console.log(`Braiins:`) + // console.log(res); + // }) + } + + + public getUserProfile() { + return this.httpService.get(`https://pool.braiins.com/accounts/profile/json/BTC`, { headers: { 'SlushPool-Auth-Token': process.env.BRAIINS_ACCESS_TOKEN } }).pipe( + map(res => res.data) + ) + } + + public getBlockRewards(from: Date, to: Date) { + return this.httpService.get(`https://pool.braiins.com/accounts/block_rewards/json/BTC?from=${from.toISOString().split('T')[0]}&to=${to.toISOString().split('T')[0]}`, { headers: { 'SlushPool-Auth-Token': process.env.BRAIINS_ACCESS_TOKEN } }).pipe( + map(res => res.data) + ); + } +} \ No newline at end of file diff --git a/src/services/btc-pay.service.ts b/src/services/btc-pay.service.ts new file mode 100644 index 0000000..d01123b --- /dev/null +++ b/src/services/btc-pay.service.ts @@ -0,0 +1,54 @@ +import { HttpService } from '@nestjs/axios'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class BTCPayService { + + constructor(private readonly httpService: HttpService) { + + } + + + public createUser() { + + const user = { + email: 'MyTestUser@gmail.com', + password: 'NOTVERYSECURE', + isAdministrator: false + } + + this.httpService.post(`/api/v1/users`, user) + } + + public createNewStore() { + const token = 'APIKEYTOKEN'; + const store = { + Name: 'STORENAME' + } + + this.httpService.post(`/api/v1/stores`, store, { headers: { Authorization: 'token ' + token } }) + } + + public createPullPayment() { + const token = 'APIKEYTOKEN'; + + const pullPayment = { + + name: 'string', + description: 'string', + amount: '0.1', + currency: 'BTC', + period: 604800, + BOLT11Expiration: 30, + autoApproveClaims: false, + startsAt: 1592312018, + expiresAt: 1593129600, + paymentMethods: [ + 'BTC' + ] + + } + + this.httpService.post(`/api/v1/stores/{storeId}/pull-payments`, pullPayment, { headers: { Authorization: 'token ' + token } }) + } +} \ No newline at end of file diff --git a/src/services/proxy.service.ts b/src/services/proxy.service.ts new file mode 100644 index 0000000..fe93bd2 --- /dev/null +++ b/src/services/proxy.service.ts @@ -0,0 +1,29 @@ +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { Server, Socket } from 'net'; + +import { ProxyClient } from '../models/ProxyClient'; + +@Injectable() +export class ProxyService implements OnModuleInit { + + constructor() { + + } + + async onModuleInit(): Promise { + if (process.env.ENABLE_PROXY == 'true') { + console.log('Connecting to braiins'); + + + const proxyServer = new Server(async (socket: Socket) => { + const proxyClient = new ProxyClient(socket); + }); + + proxyServer.listen(process.env.PROXY_PORT, () => { + console.log(`Proxy server is listening on port ${process.env.PROXY_PORT}`); + }); + } + } + + +} \ No newline at end of file diff --git a/src/services/stratum-v1-jobs.service.ts b/src/services/stratum-v1-jobs.service.ts index 415f6ef..e4dc35d 100644 --- a/src/services/stratum-v1-jobs.service.ts +++ b/src/services/stratum-v1-jobs.service.ts @@ -91,7 +91,6 @@ export class StratumV1JobsService { tempCoinbaseTx.ins[0].witness = [Buffer.alloc(32, 0)]; transactions.unshift(tempCoinbaseTx); - const transactionBuffers = transactions.map(tx => tx.getHash(false)); const merkleTree = merkle(transactionBuffers, bitcoinjs.crypto.hash256); diff --git a/src/services/stratum-v1.service.ts b/src/services/stratum-v1.service.ts index 11cdcdc..68f529e 100644 --- a/src/services/stratum-v1.service.ts +++ b/src/services/stratum-v1.service.ts @@ -28,15 +28,13 @@ export class StratumV1Service implements OnModuleInit { } - async onModuleInit(): Promise { - //await this.clientStatisticsService.deleteAll(); - - await this.clientService.deleteAll(); - - - this.startSocketServer(); + if (process.env.ENABLE_SOLO == 'true') { + //await this.clientStatisticsService.deleteAll(); + await this.clientService.deleteAll(); + this.startSocketServer(); + } } private startSocketServer() { @@ -70,8 +68,8 @@ export class StratumV1Service implements OnModuleInit { }); - server.listen(3333, () => { - console.log(`Bitcoin Stratum server is listening on port ${3333}`); + server.listen(process.env.STRATUM_PORT, () => { + console.log(`Stratum server is listening on port ${process.env.STRATUM_PORT}`); }); }