mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
fix possible dup work on diff change
This commit is contained in:
@@ -58,6 +58,7 @@ export class StratumV1Client {
|
|||||||
|
|
||||||
private buffer: string = '';
|
private buffer: string = '';
|
||||||
private connectionClosed = false;
|
private connectionClosed = false;
|
||||||
|
private lastSentMiningJobTimestamp: number = null;
|
||||||
|
|
||||||
private miningSubmissionHashes = new Set<string>()
|
private miningSubmissionHashes = new Set<string>()
|
||||||
|
|
||||||
@@ -467,6 +468,7 @@ export class StratumV1Client {
|
|||||||
if (!success) {
|
if (!success) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.lastSentMiningJobTimestamp = jobTemplate.block.timestamp;
|
||||||
|
|
||||||
|
|
||||||
//console.log(`Sent new job to ${this.clientAuthorization.worker}.${this.extraNonceAndSessionId}. (clearJobs: ${jobTemplate.blockData.clearJobs}, fee?: ${!this.noFee})`)
|
//console.log(`Sent new job to ${this.clientAuthorization.worker}.${this.extraNonceAndSessionId}. (clearJobs: ${jobTemplate.blockData.clearJobs}, fee?: ${!this.noFee})`)
|
||||||
@@ -659,9 +661,21 @@ export class StratumV1Client {
|
|||||||
await this.socket.write(data);
|
await this.socket.write(data);
|
||||||
|
|
||||||
const jobTemplate = await firstValueFrom(this.stratumV1JobsService.newMiningJob$);
|
const jobTemplate = await firstValueFrom(this.stratumV1JobsService.newMiningJob$);
|
||||||
// we need to clear the jobs so that the difficulty set takes effect. Otherwise the different miner implementations can cause issues
|
const nextTimestamp = Math.max(
|
||||||
jobTemplate.blockData.clearJobs = true;
|
jobTemplate.block.timestamp,
|
||||||
await this.sendNewMiningJob(jobTemplate);
|
Math.floor(Date.now() / 1000),
|
||||||
|
(this.lastSentMiningJobTimestamp ?? 0) + 1
|
||||||
|
);
|
||||||
|
// We need to clear jobs so the difficulty takes effect, but avoid mutating or
|
||||||
|
// re-sending the shared cached template with byte-identical work.
|
||||||
|
const refreshedJobTemplate: IJobTemplate = {
|
||||||
|
...jobTemplate,
|
||||||
|
block: Object.assign(new bitcoinjs.Block(), jobTemplate.block, {
|
||||||
|
timestamp: nextTimestamp
|
||||||
|
}),
|
||||||
|
blockData: { ...jobTemplate.blockData, clearJobs: true }
|
||||||
|
};
|
||||||
|
await this.sendNewMiningJob(refreshedJobTemplate);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe('StratumV1JobsService', () => {
|
|||||||
|
|
||||||
const createTemplate = (height = MockRecording1.BLOCK_TEMPLATE.height): IBlockTemplate => ({
|
const createTemplate = (height = MockRecording1.BLOCK_TEMPLATE.height): IBlockTemplate => ({
|
||||||
...MockRecording1.BLOCK_TEMPLATE,
|
...MockRecording1.BLOCK_TEMPLATE,
|
||||||
|
transactions: MockRecording1.BLOCK_TEMPLATE.transactions.map(tx => ({ ...tx })),
|
||||||
height
|
height
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -62,6 +63,34 @@ describe('StratumV1JobsService', () => {
|
|||||||
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should skip identical non-clean template refreshes', async () => {
|
||||||
|
await firstValueFrom(service.newMiningJob$);
|
||||||
|
|
||||||
|
blockTemplate$.next(createTemplate());
|
||||||
|
|
||||||
|
expect(service.latestJobTemplateId).toBe(2);
|
||||||
|
expect(Object.keys(service.blocks)).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit when transaction identity changes without changing transaction count', async () => {
|
||||||
|
await firstValueFrom(service.newMiningJob$);
|
||||||
|
|
||||||
|
const reorderedTemplate = createTemplate();
|
||||||
|
reorderedTemplate.transactions = [
|
||||||
|
reorderedTemplate.transactions[1],
|
||||||
|
reorderedTemplate.transactions[0],
|
||||||
|
...reorderedTemplate.transactions.slice(2)
|
||||||
|
];
|
||||||
|
|
||||||
|
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
||||||
|
blockTemplate$.next(reorderedTemplate);
|
||||||
|
const jobTemplate = await nextTemplate;
|
||||||
|
|
||||||
|
expect(jobTemplate.blockData.clearJobs).toBe(false);
|
||||||
|
expect(jobTemplate.blockData.id).toBe('2');
|
||||||
|
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
||||||
|
});
|
||||||
|
|
||||||
it('should age old jobs and templates after five minutes', async () => {
|
it('should age old jobs and templates after five minutes', async () => {
|
||||||
await firstValueFrom(service.newMiningJob$);
|
await firstValueFrom(service.newMiningJob$);
|
||||||
const oldCreation = Date.now() - (1000 * 60 * 11);
|
const oldCreation = Date.now() - (1000 * 60 * 11);
|
||||||
@@ -71,6 +100,7 @@ describe('StratumV1JobsService', () => {
|
|||||||
} as any;
|
} as any;
|
||||||
|
|
||||||
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height;
|
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height;
|
||||||
|
jest.setSystemTime(new Date(Date.now() + 1000));
|
||||||
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
||||||
blockTemplate$.next(createTemplate());
|
blockTemplate$.next(createTemplate());
|
||||||
const jobTemplate = await nextTemplate;
|
const jobTemplate = await nextTemplate;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export class StratumV1JobsService {
|
|||||||
public blocks: { [id: number]: IJobTemplate } = {};
|
public blocks: { [id: number]: IJobTemplate } = {};
|
||||||
|
|
||||||
private lastBlockHeight = 0;
|
private lastBlockHeight = 0;
|
||||||
|
private lastWorkSignature: string;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly bitcoinRpcService: BitcoinRpcService
|
private readonly bitcoinRpcService: BitcoinRpcService
|
||||||
@@ -53,13 +54,29 @@ export class StratumV1JobsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentTime = Math.floor(new Date().getTime() / 1000);
|
const currentTime = Math.floor(new Date().getTime() / 1000);
|
||||||
|
const timestamp = blockTemplate.mintime > currentTime ? blockTemplate.mintime : currentTime;
|
||||||
|
const workSignature = [
|
||||||
|
blockTemplate.previousblockhash,
|
||||||
|
blockTemplate.version,
|
||||||
|
blockTemplate.bits,
|
||||||
|
timestamp,
|
||||||
|
blockTemplate.height,
|
||||||
|
blockTemplate.coinbasevalue,
|
||||||
|
...blockTemplate.transactions.map(tx => tx.hash ?? tx.txid ?? tx.data)
|
||||||
|
].join('|');
|
||||||
|
|
||||||
|
if (!clearJobs && workSignature === this.lastWorkSignature) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
this.lastWorkSignature = workSignature;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: blockTemplate.version,
|
version: blockTemplate.version,
|
||||||
bits: parseInt(blockTemplate.bits, 16),
|
bits: parseInt(blockTemplate.bits, 16),
|
||||||
prevHash: this.convertToLittleEndian(blockTemplate.previousblockhash),
|
prevHash: this.convertToLittleEndian(blockTemplate.previousblockhash),
|
||||||
transactions: blockTemplate.transactions.map(t => bitcoinjs.Transaction.fromHex(t.data)),
|
transactions: blockTemplate.transactions.map(t => bitcoinjs.Transaction.fromHex(t.data)),
|
||||||
coinbasevalue: blockTemplate.coinbasevalue,
|
coinbasevalue: blockTemplate.coinbasevalue,
|
||||||
timestamp: blockTemplate.mintime > currentTime ? blockTemplate.mintime : currentTime,
|
timestamp,
|
||||||
networkDifficulty: this.calculateNetworkDifficulty(parseInt(blockTemplate.bits, 16)),
|
networkDifficulty: this.calculateNetworkDifficulty(parseInt(blockTemplate.bits, 16)),
|
||||||
clearJobs,
|
clearJobs,
|
||||||
height: blockTemplate.height
|
height: blockTemplate.height
|
||||||
|
|||||||
Reference in New Issue
Block a user