mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
123 lines
5.2 KiB
TypeScript
123 lines
5.2 KiB
TypeScript
import { BehaviorSubject, firstValueFrom, skip } from 'rxjs';
|
|
|
|
import { MockRecording1 } from '../../test/models/MockRecording1';
|
|
import { IBlockTemplate } from '../models/bitcoin-rpc/IBlockTemplate';
|
|
import { StratumV1JobsService } from './stratum-v1-jobs.service';
|
|
|
|
describe('StratumV1JobsService', () => {
|
|
let blockTemplate$: BehaviorSubject<IBlockTemplate>;
|
|
let bitcoinRpcService: { newBlockTemplate$: any, miningInfo: { blocks: number } };
|
|
let service: StratumV1JobsService;
|
|
let consoleLogSpy: jest.SpyInstance;
|
|
|
|
const createTemplate = (height = MockRecording1.BLOCK_TEMPLATE.height): IBlockTemplate => ({
|
|
...MockRecording1.BLOCK_TEMPLATE,
|
|
transactions: MockRecording1.BLOCK_TEMPLATE.transactions.map(tx => ({ ...tx })),
|
|
height
|
|
});
|
|
|
|
beforeEach(() => {
|
|
jest.useFakeTimers();
|
|
jest.setSystemTime(new Date(parseInt(MockRecording1.TIME, 16) * 1000));
|
|
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
|
|
blockTemplate$ = new BehaviorSubject(createTemplate());
|
|
bitcoinRpcService = {
|
|
newBlockTemplate$: blockTemplate$.asObservable(),
|
|
miningInfo: { blocks: MockRecording1.BLOCK_TEMPLATE.height }
|
|
};
|
|
service = new StratumV1JobsService(bitcoinRpcService as any);
|
|
});
|
|
|
|
afterEach(() => {
|
|
consoleLogSpy.mockRestore();
|
|
jest.useRealTimers();
|
|
});
|
|
|
|
it('should create job templates from block templates', async () => {
|
|
const jobTemplate = await firstValueFrom(service.newMiningJob$);
|
|
|
|
expect(jobTemplate.blockData).toEqual(expect.objectContaining({
|
|
id: '1',
|
|
height: MockRecording1.BLOCK_TEMPLATE.height,
|
|
clearJobs: true,
|
|
coinbasevalue: MockRecording1.BLOCK_TEMPLATE.coinbasevalue
|
|
}));
|
|
expect(jobTemplate.merkle_branch.length).toBeGreaterThan(0);
|
|
expect(jobTemplate.block.transactions[0].ins[0].witness[0]).toHaveLength(32);
|
|
expect(service.getJobTemplateById('1')).toBe(jobTemplate);
|
|
});
|
|
|
|
it('should clear jobs when the block height changes', async () => {
|
|
const firstTemplate = await firstValueFrom(service.newMiningJob$);
|
|
service.addJob({ jobId: 'old-job', creation: Date.now() } as any);
|
|
|
|
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height + 1;
|
|
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
|
blockTemplate$.next(createTemplate(MockRecording1.BLOCK_TEMPLATE.height + 1));
|
|
const jobTemplate = await nextTemplate;
|
|
|
|
expect(jobTemplate.blockData.clearJobs).toBe(true);
|
|
expect(service.getJobById('old-job')).toBeUndefined();
|
|
expect(service.getJobTemplateById(firstTemplate.blockData.id)).toBeUndefined();
|
|
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 () => {
|
|
await firstValueFrom(service.newMiningJob$);
|
|
const oldCreation = Date.now() - (1000 * 60 * 11);
|
|
service.jobs['old-job'] = { jobId: 'old-job', creation: oldCreation } as any;
|
|
service.blocks['old-template'] = {
|
|
blockData: { creation: oldCreation }
|
|
} as any;
|
|
|
|
bitcoinRpcService.miningInfo.blocks = MockRecording1.BLOCK_TEMPLATE.height;
|
|
jest.setSystemTime(new Date(Date.now() + 1000));
|
|
const nextTemplate = firstValueFrom(service.newMiningJob$.pipe(skip(1)));
|
|
blockTemplate$.next(createTemplate());
|
|
const jobTemplate = await nextTemplate;
|
|
|
|
expect(jobTemplate.blockData.clearJobs).toBe(false);
|
|
expect(service.getJobById('old-job')).toBeUndefined();
|
|
expect(service.getJobTemplateById('old-template')).toBeUndefined();
|
|
expect(service.getJobTemplateById(jobTemplate.blockData.id)).toBe(jobTemplate);
|
|
});
|
|
|
|
it('should increment job ids when jobs are added', () => {
|
|
expect(service.getNextId()).toBe('1');
|
|
|
|
service.addJob({ jobId: '1', creation: Date.now() } as any);
|
|
|
|
expect(service.getNextId()).toBe('2');
|
|
expect(service.getJobById('1')).toEqual(expect.objectContaining({ jobId: '1' }));
|
|
});
|
|
});
|