Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: derive devfee from config and calculate fee for miners #70

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ STRATUM_PORT=3333

#optional
DEV_FEE_ADDRESS=
DEV_FEE=1.5

# mainnet | testnet
NETWORK=mainnet

Expand Down
56 changes: 9 additions & 47 deletions src/models/StratumV1Client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ import { StratumV1JobsService } from '../services/stratum-v1-jobs.service';
import { IMiningInfo } from './bitcoin-rpc/IMiningInfo';
import { StratumV1Client } from './StratumV1Client';





jest.mock('../services/bitcoin-rpc.service')

jest.mock('./validators/bitcoin-address.validator', () => ({
Expand All @@ -33,10 +29,7 @@ jest.mock('./validators/bitcoin-address.validator', () => ({
},
}));


describe('StratumV1Client', () => {


let socket: Socket;
let stratumV1JobsService: StratumV1JobsService;
let bitcoinRpcService: MockBitcoinRpcService;
Expand Down Expand Up @@ -87,13 +80,9 @@ describe('StratumV1Client', () => {
}
],
}).compile();


})


});

beforeEach(async () => {

console.log('NEW TEST')

clientService = moduleRef.get<ClientService>(ClientService);
Expand All @@ -102,13 +91,10 @@ describe('StratumV1Client', () => {

dataSource.getRepository(ClientEntity).delete({});
dataSource.getRepository(ClientStatisticsEntity).delete({});



clientStatisticsService = moduleRef.get<ClientStatisticsService>(ClientStatisticsService);

configService = moduleRef.get<ConfigService>(ConfigService);


bitcoinRpcService = new MockBitcoinRpcService(configService,null);
jest.spyOn(bitcoinRpcService, 'getBlockTemplate').mockReturnValue(Promise.resolve(MockRecording1.BLOCK_TEMPLATE));
bitcoinRpcService.newBlock$ = newBlockEmitter.asObservable();
Expand All @@ -130,7 +116,6 @@ describe('StratumV1Client', () => {

const addressSettings = moduleRef.get<AddressSettingsService>(AddressSettingsService);


client = new StratumV1Client(
socket,
stratumV1JobsService,
Expand All @@ -153,7 +138,6 @@ describe('StratumV1Client', () => {
jest.useRealTimers();
})


it('should subscribe to socket', () => {
expect(socket.on).toHaveBeenCalled();
});
Expand All @@ -173,10 +157,8 @@ describe('StratumV1Client', () => {
await new Promise((r) => setTimeout(r, 1));

expect(socket.write).toHaveBeenCalledWith(`{"id":1,"error":null,"result":[[["mining.notify","${client.extraNonceAndSessionId}"]],"${client.extraNonceAndSessionId}",4]}\n`, expect.any(Function));

});


it('should respond to mining.configure', async () => {

jest.spyOn(socket, 'write').mockImplementation((data) => true);
Expand Down Expand Up @@ -227,48 +209,28 @@ describe('StratumV1Client', () => {

const clientCount = await clientService.connectedClientCount();
expect(clientCount).toBe(1);

});





it('should send job and accept submission', async () => {



const date = new Date(parseInt(MockRecording1.TIME, 16) * 1000);



jest.setSystemTime(date);

jest.spyOn(client as any, 'write').mockImplementation((data) => Promise.resolve(true));



socketEmitter(Buffer.from(MockRecording1.MINING_SUBSCRIBE));
socketEmitter(Buffer.from(MockRecording1.MINING_SUGGEST_DIFFICULTY));
socketEmitter(Buffer.from(MockRecording1.MINING_AUTHORIZE));




await new Promise((r) => setTimeout(r, 100));





expect((client as any).write).lastCalledWith(`{"id":null,"method":"mining.notify","params":["1","171592f223740e92d223f6e68bff25279af7ac4f2246451e0000000200000000","02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1903c943255c7075626c69632d706f6f6c5c","ffffffff037a90000000000000160014e6f22ca44dc800e9d049621a3b9a42c509f1c4bc3b0f250000000000160014e6f22ca44dc800e9d049621a3b9a42c509f1c4bc0000000000000000266a24aa21a9edbd3d1d916aa0b57326a2d88ebe1b68a1d7c48585f26d8335fe6a94b62755f64c00000000",["175335649d5e8746982969ec88f52e85ac9917106fba5468e699c8879ab974a1","d5644ab3e708c54cd68dc5aedc92b8d3037449687f92ec41ed6e37673d969d4a","5c9ec187517edc0698556cca5ce27e54c96acb014770599ed9df4d4937fbf2b0"],"20000000","192495f8","${MockRecording1.TIME}",false]}\n`);



socketEmitter(Buffer.from(MockRecording1.MINING_SUBMIT));

jest.useRealTimers();
await new Promise((r) => setTimeout(r, 1000));

expect((client as any).write).lastCalledWith(`{\"id\":5,\"error\":null,\"result\":true}\n`);


});



});
61 changes: 27 additions & 34 deletions src/models/StratumV1Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export class StratumV1Client {

public extraNonceAndSessionId: string;
public sessionStart: Date;
public noFee: boolean;
public shouldApplyFee: boolean = false;
public hashRate: number = 0;

private buffer: string = '';
Expand Down Expand Up @@ -82,8 +82,6 @@ export class StratumV1Client {
}
});
});


}

public async destroy() {
Expand Down Expand Up @@ -121,9 +119,7 @@ export class StratumV1Client {
await this.socket.end();
return;
}




switch (parsedMessage.method) {
case eRequestMethod.SUBSCRIBE: {
const subscriptionMessage = plainToInstance(
Expand All @@ -139,7 +135,6 @@ export class StratumV1Client {
const errors = await validate(subscriptionMessage, validatorOptions);

if (errors.length === 0) {

if (this.sessionStart == null) {
this.sessionStart = new Date();
this.statistics = new StratumV1ClientStatistics(this.clientStatisticsService);
Expand Down Expand Up @@ -168,8 +163,8 @@ export class StratumV1Client {

break;
}

case eRequestMethod.CONFIGURE: {

const configurationMessage = plainToInstance(
ConfigurationMessage,
parsedMessage,
Expand Down Expand Up @@ -206,8 +201,8 @@ export class StratumV1Client {

break;
}

case eRequestMethod.AUTHORIZE: {

const authorizationMessage = plainToInstance(
AuthorizationMessage,
parsedMessage,
Expand All @@ -219,7 +214,6 @@ export class StratumV1Client {
};

const errors = await validate(authorizationMessage, validatorOptions);

if (errors.length === 0) {
this.clientAuthorization = authorizationMessage;
const success = await this.write(JSON.stringify(this.clientAuthorization.response()) + '\n');
Expand All @@ -242,6 +236,7 @@ export class StratumV1Client {

break;
}

case eRequestMethod.SUGGEST_DIFFICULTY: {
if (this.usedSuggestedDifficulty == true) {
return;
Expand All @@ -258,7 +253,6 @@ export class StratumV1Client {
};

const errors = await validate(suggestDifficultyMessage, validatorOptions);

if (errors.length === 0) {

this.clientSuggestedDifficulty = suggestDifficultyMessage;
Expand All @@ -284,7 +278,6 @@ export class StratumV1Client {
break;
}
case eRequestMethod.SUBMIT: {

if (this.stratumInitialized == false) {
console.log('Submit before initalized');
await this.socket.end();
Expand All @@ -303,7 +296,6 @@ export class StratumV1Client {
};

const errors = await validate(miningSubmitMessage, validatorOptions);

if (errors.length === 0 && this.stratumInitialized == true) {
const result = await this.handleMiningSubmission(miningSubmitMessage);
if (result == true) {
Expand All @@ -312,8 +304,6 @@ export class StratumV1Client {
return;
}
}


} else {
console.log('Mining Submit validation error');
const err = new StratumErrorMessage(
Expand All @@ -336,14 +326,11 @@ export class StratumV1Client {
// return;
// }
}



if (this.clientSubscription != null
&& this.clientAuthorization != null
&& this.stratumInitialized == false) {

await this.initStratum();

}
}

Expand Down Expand Up @@ -381,26 +368,26 @@ export class StratumV1Client {
);

}

private async sendNewMiningJob(jobTemplate: IJobTemplate) {

let payoutInformation;
const devFee = this.configService.get('DEV_FEE');
const devFeeAddress = this.configService.get('DEV_FEE_ADDRESS');
//50Th/s
this.noFee = false;

if (this.entity) {
this.hashRate = await this.clientStatisticsService.getHashRateForSession(this.clientAuthorization.address, this.clientAuthorization.worker, this.extraNonceAndSessionId);
this.noFee = this.hashRate != 0 && this.hashRate < 50000000000000;
this.shouldApplyFee = this.hashRate != 0 && this.hashRate > 50000000000000; // 50Th/s
}
if (this.noFee || devFeeAddress == null || devFeeAddress.length < 1) {

const applyDevFee = this.shouldApplyFee && devFeeAddress && devFeeAddress.length > 0 && !isNaN(devFee) && devFee > 0;
if (applyDevFee) {
payoutInformation = [
{ address: this.clientAuthorization.address, percent: 100 }
{ address: devFeeAddress, percent: devFee },
{ address: this.clientAuthorization.address, percent: this.calculateMinerFeeWithDevFee(devFee) }
];

} else {
payoutInformation = [
{ address: devFeeAddress, percent: 1.5 },
{ address: this.clientAuthorization.address, percent: 98.5 }
{ address: this.clientAuthorization.address, percent: 100 }
];
}

Expand All @@ -426,18 +413,24 @@ export class StratumV1Client {
);

this.stratumV1JobsService.addJob(job);



const success = await this.write(job.response(jobTemplate));
if (!success) {
return;
}



//console.log(`Sent new job to ${this.clientAuthorization.worker}.${this.extraNonceAndSessionId}. (clearJobs: ${jobTemplate.blockData.clearJobs}, fee?: ${!this.noFee})`)

}

private calculateMinerFeeWithDevFee(devFee: number): number {
const maxFee = 100;
const defaultFee = 1.5;

// Ensure the devFee is below 100, otherwise default to 1.5
const feeToUse = devFee > maxFee ? defaultFee : devFee;

return maxFee - feeToUse;
}

private async handleMiningSubmission(submission: MiningSubmitMessage) {

Expand Down