mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 17:15:03 -07:00
cleanup and change sewit witness output to its own tx
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||
|
||||
import { DateTimeTransformer } from '../utils/DateTimeTransformer';
|
||||
import { TrackedEntity } from '../utils/TrackedEntity.entity';
|
||||
@@ -6,8 +6,8 @@ import { TrackedEntity } from '../utils/TrackedEntity.entity';
|
||||
@Entity()
|
||||
export class ClientStatisticsEntity extends TrackedEntity {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@PrimaryColumn({ length: 64, type: 'varchar' })
|
||||
submissionHash: string;
|
||||
|
||||
@Column({ length: 62, type: 'varchar' })
|
||||
address: string;
|
||||
|
||||
@@ -6,6 +6,12 @@ import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
|
||||
if (process.env.PORT == null) {
|
||||
console.error('It appears your environment is not configured, create and populate an .env file.');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, new FastifyAdapter());
|
||||
app.setGlobalPrefix('api')
|
||||
app.useGlobalPipes(
|
||||
|
||||
+14
-17
@@ -12,17 +12,18 @@ interface AddressObject {
|
||||
}
|
||||
export class MiningJob {
|
||||
|
||||
private coinbasePart1: string;
|
||||
private coinbasePart2: string;
|
||||
|
||||
private merkle_branch: string[]; // List of hashes, will be used for calculation of merkle root. This is not a list of all transactions, it only contains prepared hashes of steps of merkle tree algorithm.
|
||||
|
||||
public jobId: string; // ID of the job. Use this ID while submitting share generated from this job.
|
||||
public response: string;
|
||||
public block: bitcoinjs.Block = new bitcoinjs.Block();
|
||||
public networkDifficulty: number;
|
||||
|
||||
constructor(id: string, payoutInformation: AddressObject[], public blockTemplate: IBlockTemplate, public clean_jobs: boolean) {
|
||||
|
||||
this.jobId = id;
|
||||
//this.target = blockTemplate.target;
|
||||
this.block.prevHash = this.convertToLittleEndian(blockTemplate.previousblockhash);
|
||||
|
||||
this.block.version = blockTemplate.version;
|
||||
@@ -51,7 +52,8 @@ export class MiningJob {
|
||||
// 32-byte - Commitment hash: Double-SHA256(witness root hash|witness reserved value)
|
||||
const commitmentHash = this.sha256(this.sha256(this.block.witnessCommit));
|
||||
// 39th byte onwards: Optional data with no consensus meaning
|
||||
coinbaseTransaction.ins[0].script = bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.concat([blockHeightScript, segwitMagicBits, commitmentHash, Buffer.from('00000000' + '00000000', 'hex')])]);
|
||||
coinbaseTransaction.ins[0].script = bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.concat([blockHeightScript, Buffer.from('00000000' + '00000000', 'hex')])]);
|
||||
coinbaseTransaction.addOutput(bitcoinjs.script.compile([bitcoinjs.opcodes.OP_RETURN, Buffer.concat([segwitMagicBits, commitmentHash])]), 0);
|
||||
|
||||
// get the non-witness coinbase tx
|
||||
//@ts-ignore
|
||||
@@ -63,8 +65,8 @@ export class MiningJob {
|
||||
|
||||
const coinbasePart1 = serializedCoinbaseTx.slice(0, partOneIndex);
|
||||
const coinbasePart2 = serializedCoinbaseTx.slice(partOneIndex);
|
||||
const coinb1 = coinbasePart1.slice(0, coinbasePart1.length - 16);
|
||||
const coinb2 = coinbasePart2;
|
||||
this.coinbasePart1 = coinbasePart1.slice(0, coinbasePart1.length - 16);
|
||||
this.coinbasePart2 = coinbasePart2;
|
||||
|
||||
|
||||
// Calculate merkle branch
|
||||
@@ -75,18 +77,15 @@ export class MiningJob {
|
||||
|
||||
this.block.transactions[0] = coinbaseTransaction;
|
||||
|
||||
this.constructResponse(coinb1, coinb2);
|
||||
|
||||
}
|
||||
|
||||
public copyAndUpdateBlock(versionMaskString: number, nonce: number, extraNonce: string, extraNonce2: string, timestamp: number): bitcoinjs.Block {
|
||||
public copyAndUpdateBlock(versionMask: number, nonce: number, extraNonce: string, extraNonce2: string, timestamp: number): bitcoinjs.Block {
|
||||
|
||||
const testBlock = bitcoinjs.Block.fromBuffer(this.block.toBuffer());
|
||||
|
||||
testBlock.nonce = nonce;
|
||||
|
||||
// recompute version mask
|
||||
const versionMask = versionMaskString;
|
||||
if (versionMask !== undefined && versionMask != 0) {
|
||||
testBlock.version = (testBlock.version ^ versionMask);
|
||||
}
|
||||
@@ -131,8 +130,6 @@ export class MiningJob {
|
||||
// Set the version of the transaction
|
||||
coinbaseTransaction.version = 2;
|
||||
|
||||
|
||||
|
||||
// Add the coinbase input (input with no previous output)
|
||||
coinbaseTransaction.addInput(Buffer.from('0000000000000000000000000000000000000000000000000000000000000000', 'hex'), 0xffffffff, 0xffffffff);
|
||||
|
||||
@@ -145,7 +142,7 @@ export class MiningJob {
|
||||
coinbaseTransaction.addOutput(this.getPaymentScript(recipientAddress.address), amount);
|
||||
})
|
||||
|
||||
//Add any remaining sats from the Math.floor
|
||||
//Add any remaining sats from the Math.floor
|
||||
coinbaseTransaction.outs[0].value += rewardBalance;
|
||||
|
||||
const segwitWitnessReservedValue = Buffer.alloc(32, 0);
|
||||
@@ -160,7 +157,7 @@ export class MiningJob {
|
||||
const addressInfo = getAddressInfo(address);
|
||||
switch (addressInfo.type) {
|
||||
case AddressType.p2wpkh: {
|
||||
return bitcoinjs.payments.p2wpkh({ address, network: bitcoinjs.networks.testnet }).output;
|
||||
return bitcoinjs.payments.p2wpkh({ address }).output;
|
||||
}
|
||||
case AddressType.p2pkh: {
|
||||
return bitcoinjs.payments.p2pkh({ address }).output;
|
||||
@@ -185,7 +182,7 @@ export class MiningJob {
|
||||
}
|
||||
|
||||
|
||||
private constructResponse(coinb1: string, coinb2: string) {
|
||||
public response(): string {
|
||||
|
||||
const job = {
|
||||
id: null,
|
||||
@@ -193,8 +190,8 @@ export class MiningJob {
|
||||
params: [
|
||||
this.jobId,
|
||||
this.swapEndianWords(this.block.prevHash).toString('hex'),
|
||||
coinb1,
|
||||
coinb2,
|
||||
this.coinbasePart1,
|
||||
this.coinbasePart2,
|
||||
this.merkle_branch,
|
||||
this.block.version.toString(16),
|
||||
this.block.bits.toString(16),
|
||||
@@ -203,7 +200,7 @@ export class MiningJob {
|
||||
]
|
||||
};
|
||||
|
||||
this.response = JSON.stringify(job);
|
||||
return JSON.stringify(job) + '\n';
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { plainToInstance } from 'class-transformer';
|
||||
import { validate, ValidatorOptions } from 'class-validator';
|
||||
import * as crypto from 'crypto';
|
||||
import { Socket } from 'net';
|
||||
import PromiseSocket from 'promise-socket';
|
||||
import { combineLatest, firstValueFrom, interval, startWith, takeUntil } from 'rxjs';
|
||||
|
||||
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
|
||||
@@ -11,7 +12,7 @@ import { ClientService } from '../ORM/client/client.service';
|
||||
import { BitcoinRpcService } from '../services/bitcoin-rpc.service';
|
||||
import { BlockTemplateService } from '../services/block-template.service';
|
||||
import { StratumV1JobsService } from '../services/stratum-v1-jobs.service';
|
||||
import { EasyUnsubscribe } from '../utils/AutoUnsubscribe';
|
||||
import { EasyUnsubscribe } from '../utils/EasyUnsubscribe';
|
||||
import { IBlockTemplate } from './bitcoin-rpc/IBlockTemplate';
|
||||
import { eRequestMethod } from './enums/eRequestMethod';
|
||||
import { eResponseMethod } from './enums/eResponseMethod';
|
||||
@@ -25,8 +26,6 @@ import { SubscriptionMessage } from './stratum-messages/SubscriptionMessage';
|
||||
import { SuggestDifficulty } from './stratum-messages/SuggestDifficultyMessage';
|
||||
import { StratumV1ClientStatistics } from './StratumV1ClientStatistics';
|
||||
|
||||
|
||||
|
||||
export class StratumV1Client extends EasyUnsubscribe {
|
||||
|
||||
private clientSubscription: SubscriptionMessage;
|
||||
@@ -43,7 +42,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
public extraNonce: string;
|
||||
|
||||
constructor(
|
||||
public readonly socket: Socket,
|
||||
public readonly promiseSocket: PromiseSocket<Socket>,
|
||||
private readonly stratumV1JobsService: StratumV1JobsService,
|
||||
private readonly blockTemplateService: BlockTemplateService,
|
||||
private readonly bitcoinRpcService: BitcoinRpcService,
|
||||
@@ -57,7 +56,12 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
|
||||
console.log(`New client ID: : ${this.extraNonce}`);
|
||||
|
||||
this.socket.on('data', this.handleData.bind(this, this.socket));
|
||||
this.promiseSocket.socket.on('data', (data: Buffer) => {
|
||||
data.toString()
|
||||
.split('\n')
|
||||
.filter(m => m.length > 0)
|
||||
.forEach(m => this.handleMessage(m))
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -68,17 +72,8 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
return hexString;
|
||||
}
|
||||
|
||||
private async handleData(socket: Socket, data: Buffer) {
|
||||
const message = data.toString();
|
||||
|
||||
message.split('\n')
|
||||
.filter(m => m.length > 0)
|
||||
.forEach(this.handleMessage.bind(this, socket));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async handleMessage(socket: Socket, message: string) {
|
||||
private async handleMessage(message: string) {
|
||||
console.log('Received:', message);
|
||||
|
||||
// Parse the message and check if it's the initial subscription message
|
||||
@@ -87,7 +82,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
parsedMessage = JSON.parse(message);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.socket.end();
|
||||
this.promiseSocket.end();
|
||||
}
|
||||
|
||||
switch (parsedMessage.method) {
|
||||
@@ -107,18 +102,15 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
if (errors.length === 0) {
|
||||
this.clientSubscription = subscriptionMessage;
|
||||
|
||||
socket.write(JSON.stringify(this.clientSubscription.response(this.extraNonce)) + '\n');
|
||||
await this.promiseSocket.write(JSON.stringify(this.clientSubscription.response(this.extraNonce)) + '\n');
|
||||
} else {
|
||||
|
||||
const err = new StratumErrorMessage(
|
||||
subscriptionMessage.id,
|
||||
eStratumErrorCode.OtherUnknown,
|
||||
'Subscription error',
|
||||
errors).response();
|
||||
|
||||
console.error(err);
|
||||
socket.write(err);
|
||||
|
||||
await this.promiseSocket.write(err);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -140,9 +132,15 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
if (errors.length === 0) {
|
||||
this.clientConfiguration = configurationMessage;
|
||||
//const response = this.buildSubscriptionResponse(configurationMessage.id);
|
||||
socket.write(JSON.stringify(this.clientConfiguration.response()) + '\n');
|
||||
await this.promiseSocket.write(JSON.stringify(this.clientConfiguration.response()) + '\n');
|
||||
} else {
|
||||
console.error(errors);
|
||||
const err = new StratumErrorMessage(
|
||||
configurationMessage.id,
|
||||
eStratumErrorCode.OtherUnknown,
|
||||
'Configuration error',
|
||||
errors).response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -164,9 +162,15 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
this.clientAuthorization = authorizationMessage;
|
||||
|
||||
//const response = this.buildSubscriptionResponse(authorizationMessage.id);
|
||||
socket.write(JSON.stringify(this.clientAuthorization.response()) + '\n');
|
||||
await this.promiseSocket.write(JSON.stringify(this.clientAuthorization.response()) + '\n');
|
||||
} else {
|
||||
console.error(errors);
|
||||
const err = new StratumErrorMessage(
|
||||
authorizationMessage.id,
|
||||
eStratumErrorCode.OtherUnknown,
|
||||
'Authorization error',
|
||||
errors).response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -192,10 +196,16 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
|
||||
this.clientSuggestedDifficulty = suggestDifficultyMessage;
|
||||
this.sessionDifficulty = suggestDifficultyMessage.suggestedDifficulty;
|
||||
socket.write(JSON.stringify(this.clientSuggestedDifficulty.response(this.sessionDifficulty)) + '\n');
|
||||
await this.promiseSocket.write(JSON.stringify(this.clientSuggestedDifficulty.response(this.sessionDifficulty)) + '\n');
|
||||
this.usedSuggestedDifficulty = true;
|
||||
} else {
|
||||
console.error(errors);
|
||||
const err = new StratumErrorMessage(
|
||||
suggestDifficultyMessage.id,
|
||||
eStratumErrorCode.OtherUnknown,
|
||||
'Suggest difficulty error',
|
||||
errors).response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -214,11 +224,17 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
|
||||
if (errors.length === 0) {
|
||||
await this.handleMiningSubmission(miningSubmitMessage);
|
||||
socket.write(JSON.stringify(miningSubmitMessage.response()) + '\n');
|
||||
await this.promiseSocket.write(JSON.stringify(miningSubmitMessage.response()) + '\n');
|
||||
|
||||
|
||||
} else {
|
||||
console.error(errors);
|
||||
const err = new StratumErrorMessage(
|
||||
miningSubmitMessage.id,
|
||||
eStratumErrorCode.OtherUnknown,
|
||||
'Suggest difficulty error',
|
||||
errors).response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -233,7 +249,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
if (this.clientSuggestedDifficulty == null) {
|
||||
console.log(`Setting difficulty to ${this.sessionDifficulty}`)
|
||||
const setDifficulty = JSON.stringify(new SuggestDifficulty().response(this.sessionDifficulty));
|
||||
this.socket.write(setDifficulty + '\n');
|
||||
await this.promiseSocket.write(setDifficulty + '\n');
|
||||
}
|
||||
|
||||
this.stratumInitialized = true;
|
||||
@@ -264,7 +280,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
|
||||
lastIntervalCount = interValCount;
|
||||
|
||||
this.sendNewMiningJob(blockTemplate, clearJobs);
|
||||
await this.sendNewMiningJob(blockTemplate, clearJobs);
|
||||
|
||||
await this.checkDifficulty();
|
||||
|
||||
@@ -274,7 +290,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
}
|
||||
}
|
||||
|
||||
private sendNewMiningJob(blockTemplate: IBlockTemplate, clearJobs: boolean) {
|
||||
private async sendNewMiningJob(blockTemplate: IBlockTemplate, clearJobs: boolean) {
|
||||
|
||||
// const payoutInformation = [
|
||||
// { address: 'bc1q99n3pu025yyu0jlywpmwzalyhm36tg5u37w20d', percent: 1.8 },
|
||||
@@ -288,10 +304,9 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
const job = new MiningJob(this.stratumV1JobsService.getNextId(), payoutInformation, blockTemplate, clearJobs);
|
||||
|
||||
this.stratumV1JobsService.addJob(job, clearJobs);
|
||||
;
|
||||
|
||||
const data = job.response + '\n';
|
||||
|
||||
this.socket.write(data);
|
||||
await this.promiseSocket.write(job.response());
|
||||
|
||||
console.log(`Sent new job to ${this.extraNonce}. (clearJobs: ${clearJobs})`)
|
||||
|
||||
@@ -301,9 +316,14 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
private async handleMiningSubmission(submission: MiningSubmitMessage) {
|
||||
|
||||
const job = this.stratumV1JobsService.getJobById(submission.jobId);
|
||||
// a miner may submit a job that doesn't exist anymore if it was removed by a new block notification
|
||||
// a miner may submit a job that doesn't exist anymore if it was removed by a new block notification
|
||||
if (job == null) {
|
||||
console.log('job not found')
|
||||
const err = new StratumErrorMessage(
|
||||
submission.id,
|
||||
eStratumErrorCode.JobNotFound,
|
||||
'Job not found').response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
return;
|
||||
}
|
||||
const updatedJobBlock = job.copyAndUpdateBlock(
|
||||
@@ -313,7 +333,7 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
submission.extraNonce2,
|
||||
parseInt(submission.ntime, 16)
|
||||
);
|
||||
const submissionDifficulty = this.calculateDifficulty(updatedJobBlock.toBuffer(true));
|
||||
const { submissionDifficulty, submissionHash } = this.calculateDifficulty(updatedJobBlock.toBuffer(true));
|
||||
|
||||
console.log(`DIFF: ${Math.round(submissionDifficulty)} of ${this.sessionDifficulty} from ${this.clientAuthorization.worker + '.' + this.extraNonce}`);
|
||||
|
||||
@@ -324,15 +344,30 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
const blockHex = updatedJobBlock.toHex(false);
|
||||
this.bitcoinRpcService.SUBMIT_BLOCK(blockHex);
|
||||
}
|
||||
try {
|
||||
await this.statistics.addSubmission(this.entity, submissionHash, this.sessionDifficulty);
|
||||
} catch (e) {
|
||||
const err = new StratumErrorMessage(
|
||||
submission.id,
|
||||
eStratumErrorCode.DuplicateShare,
|
||||
'Duplicate share').response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
|
||||
await this.statistics.addSubmission(this.entity, this.sessionDifficulty);
|
||||
if (submissionDifficulty > this.entity.bestDifficulty) {
|
||||
await this.clientService.updateBestDifficulty(this.extraNonce, submissionDifficulty);
|
||||
this.entity.bestDifficulty = submissionDifficulty;
|
||||
if (submissionDifficulty > this.entity.bestDifficulty) {
|
||||
await this.clientService.updateBestDifficulty(this.extraNonce, submissionDifficulty);
|
||||
this.entity.bestDifficulty = submissionDifficulty;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log(`Difficulty too low`);
|
||||
const err = new StratumErrorMessage(
|
||||
submission.id,
|
||||
eStratumErrorCode.LowDifficultyShare,
|
||||
'Difficulty too low').response();
|
||||
console.error(err);
|
||||
await this.promiseSocket.write(err);
|
||||
|
||||
}
|
||||
|
||||
await this.checkDifficulty();
|
||||
@@ -355,19 +390,16 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
params: [targetDiff]
|
||||
}) + '\n';
|
||||
|
||||
this.socket.write(data);
|
||||
await this.promiseSocket.write(data);
|
||||
|
||||
// we need to clear the jobs so that the difficulty set takes effect. Otherwise the different miner implementations can cause issues
|
||||
const { blockTemplate } = await firstValueFrom(this.blockTemplateService.currentBlockTemplate$);
|
||||
this.sendNewMiningJob(blockTemplate, true);
|
||||
|
||||
|
||||
|
||||
await this.sendNewMiningJob(blockTemplate, true);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public calculateDifficulty(header: Buffer): number {
|
||||
public calculateDifficulty(header: Buffer): { submissionDifficulty: number, submissionHash: string } {
|
||||
|
||||
const hashBuffer: Buffer = crypto.createHash('sha256').update(header).digest();
|
||||
const hashResult: Buffer = crypto.createHash('sha256').update(hashBuffer).digest();
|
||||
@@ -375,7 +407,8 @@ export class StratumV1Client extends EasyUnsubscribe {
|
||||
let s64 = this.le256todouble(hashResult);
|
||||
|
||||
const truediffone = Big('26959535291011309493156476344723991336010898738574164086137773096960');
|
||||
return truediffone.div(s64.toString()).toNumber();
|
||||
const difficulty = truediffone.div(s64.toString()).toNumber();
|
||||
return { submissionDifficulty: difficulty, submissionHash: hashResult.toString('hex') };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export class StratumV1ClientStatistics {
|
||||
this.submissionCacheStart = new Date();
|
||||
}
|
||||
|
||||
public async addSubmission(client: ClientEntity, targetDifficulty: number) {
|
||||
public async addSubmission(client: ClientEntity, submissionHash: string, targetDifficulty: number) {
|
||||
|
||||
if (this.submissionCache.length > CACHE_SIZE) {
|
||||
this.submissionCache.shift();
|
||||
@@ -29,6 +29,7 @@ export class StratumV1ClientStatistics {
|
||||
address: client.address,
|
||||
clientName: client.clientName,
|
||||
sessionId: client.sessionId,
|
||||
submissionHash
|
||||
});
|
||||
|
||||
}
|
||||
@@ -37,7 +38,7 @@ export class StratumV1ClientStatistics {
|
||||
|
||||
// miner hasn't submitted shares in one minute
|
||||
if (this.submissionCache.length == 0 && (new Date().getTime() - this.submissionCacheStart.getTime()) / 1000 > 60) {
|
||||
return this.blpo2(clientDifficulty >> 1);
|
||||
return this.nearestPowerOfTwo(clientDifficulty >> 1);
|
||||
}
|
||||
|
||||
if (this.submissionCache.length < CACHE_SIZE) {
|
||||
@@ -55,15 +56,14 @@ export class StratumV1ClientStatistics {
|
||||
const targetDifficulty = difficultyPerSecond * TARGET_SUBMISSION_PER_SECOND;
|
||||
|
||||
if (clientDifficulty << 1 < targetDifficulty || clientDifficulty >> 1 > targetDifficulty) {
|
||||
return this.blpo2(targetDifficulty)
|
||||
return this.nearestPowerOfTwo(targetDifficulty)
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
|
||||
}
|
||||
|
||||
private blpo2(val) {
|
||||
private nearestPowerOfTwo(val) {
|
||||
let x = val | (val >> 1);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 4);
|
||||
@@ -86,5 +86,4 @@ export class StratumV1ClientStatistics {
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@ export class StratumErrorMessage {
|
||||
private id: number = null,
|
||||
private errorCode: eStratumErrorCode,
|
||||
private errorMessage: string,
|
||||
private validationErrors: ValidationError[]
|
||||
private validationErrors: ValidationError[] = []
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ import { BehaviorSubject, filter } from 'rxjs';
|
||||
import { IBlockTemplate } from '../models/bitcoin-rpc/IBlockTemplate';
|
||||
import { IMiningInfo } from '../models/bitcoin-rpc/IMiningInfo';
|
||||
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class BitcoinRpcService {
|
||||
|
||||
@@ -25,10 +23,8 @@ export class BitcoinRpcService {
|
||||
|
||||
this.client = new RPCClient({ url, port, timeout, user, pass });
|
||||
|
||||
|
||||
console.log('Bitcoin RPC connected');
|
||||
|
||||
|
||||
// Maybe use ZeroMQ ?
|
||||
setInterval(async () => {
|
||||
const miningInfo = await this.getMiningInfo();
|
||||
@@ -37,16 +33,12 @@ export class BitcoinRpcService {
|
||||
this._newBlock$.next(miningInfo);
|
||||
|
||||
this.blockHeight = miningInfo.blocks;
|
||||
|
||||
}
|
||||
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async getBlockTemplate(): Promise<IBlockTemplate> {
|
||||
|
||||
const result: IBlockTemplate = await this.client.getblocktemplate({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { Server, Socket } from 'net';
|
||||
import { PromiseSocket } from 'promise-socket';
|
||||
|
||||
import { StratumV1Client } from '../models/StratumV1Client';
|
||||
import { ClientStatisticsService } from '../ORM/client-statistics/client-statistics.service';
|
||||
@@ -12,8 +13,6 @@ import { StratumV1JobsService } from './stratum-v1-jobs.service';
|
||||
@Injectable()
|
||||
export class StratumV1Service implements OnModuleInit {
|
||||
|
||||
// public clients: StratumV1Client[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly bitcoinRpcService: BitcoinRpcService,
|
||||
private readonly blockTemplateService: BlockTemplateService,
|
||||
@@ -29,38 +28,36 @@ export class StratumV1Service implements OnModuleInit {
|
||||
await this.clientService.deleteAll();
|
||||
|
||||
this.startSocketServer();
|
||||
|
||||
}
|
||||
|
||||
private startSocketServer() {
|
||||
new Server(async (socket: Socket) => {
|
||||
new Server(async (s: Socket) => {
|
||||
|
||||
const promiseSocket = new PromiseSocket(s);
|
||||
|
||||
const client = new StratumV1Client(socket, new StratumV1JobsService(), this.blockTemplateService, this.bitcoinRpcService, this.clientService, this.clientStatisticsService);
|
||||
const client = new StratumV1Client(promiseSocket, new StratumV1JobsService(), this.blockTemplateService, this.bitcoinRpcService, this.clientService, this.clientStatisticsService);
|
||||
|
||||
|
||||
const clientCount = await this.clientService.connectedClientCount();
|
||||
|
||||
//this.clients.push(client);
|
||||
console.log(`New client connected: ${promiseSocket.socket.remoteAddress}, ${clientCount} total clients`);
|
||||
|
||||
console.log(`New client connected: ${socket.remoteAddress}, ${clientCount} total clients`);
|
||||
|
||||
socket.on('end', async () => {
|
||||
promiseSocket.socket.on('end', async (error: Error) => {
|
||||
// Handle socket disconnection
|
||||
client.destroy();
|
||||
await this.clientService.delete(client.extraNonce);
|
||||
|
||||
const clientCount = await this.clientService.connectedClientCount();
|
||||
console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`);
|
||||
console.log(`Client disconnected: ${promiseSocket.socket.remoteAddress}, ${clientCount} total clients`);
|
||||
});
|
||||
|
||||
socket.on('error', async (error: Error) => {
|
||||
promiseSocket.socket.on('error', async (error: Error) => {
|
||||
|
||||
client.destroy();
|
||||
await this.clientService.delete(client.extraNonce);
|
||||
const clientCount = await this.clientService.connectedClientCount();
|
||||
console.error(`Socket error:`, error);
|
||||
console.log(`Client disconnected: ${socket.remoteAddress}, ${clientCount} total clients`);
|
||||
console.log(`Client disconnected: ${promiseSocket.socket.remoteAddress}, ${clientCount} total clients`);
|
||||
|
||||
});
|
||||
|
||||
@@ -70,6 +67,4 @@ export class StratumV1Service implements OnModuleInit {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user