mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
coinbase calculations
This commit is contained in:
+81
-62
@@ -1,8 +1,9 @@
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
import { eResponseMethod } from './enums/eResponseMethod';
|
||||
import { IBlockTempalte } from './IBlockTempalte';
|
||||
import { IBlockTemplate, IBlockTemplateTx } from './bitcoin-rpc/IBlockTemplate';
|
||||
import { randomUUID } from 'crypto';
|
||||
import { MerkleTree } from 'merkletreejs'
|
||||
|
||||
export class MiningJob {
|
||||
public id: number;
|
||||
@@ -20,7 +21,7 @@ export class MiningJob {
|
||||
public ntime: string; // Current ntime/
|
||||
public clean_jobs: boolean; // When true, server indicates that submitting shares from previous jobs don't have a sense and such shares will be rejected. When this flag is set, miner should also drop all previous jobs too.
|
||||
|
||||
constructor(blockTemplate: IBlockTempalte) {
|
||||
constructor(blockTemplate: IBlockTemplate) {
|
||||
|
||||
this.job_id = randomUUID();
|
||||
this.prevhash = blockTemplate.previousblockhash;
|
||||
@@ -31,27 +32,97 @@ export class MiningJob {
|
||||
this.clean_jobs = false;
|
||||
|
||||
|
||||
// Construct coinbase transaction
|
||||
const coinbaseTransaction = blockTemplate.transactions[0];
|
||||
const coinbaseHashBin = this.buildCoinbaseHashBin(coinbaseTransaction.data);
|
||||
this.coinb1 = this.coinbasePrefix(coinbaseTransaction.data, coinbaseHashBin);
|
||||
this.coinb2 = ''; // Assuming no suffix is required
|
||||
const { coinb1, coinb2, coinbaseData } = this.createCoinbaseTransaction('', blockTemplate.height);
|
||||
|
||||
console.log(coinbaseData);
|
||||
const coinbaseHash = this.bufferToHex(this.sha256(coinbaseData));
|
||||
|
||||
this.coinb1 = coinb1;
|
||||
this.coinb2 = coinb2;
|
||||
|
||||
const transactions = blockTemplate.transactions.map(tx => tx.hash);
|
||||
const transactionFees = blockTemplate.transactions.reduce((pre, cur, i, arr) => {
|
||||
return pre + cur.fee;
|
||||
}, 0);
|
||||
console.log('TRANSACTION FEES', transactionFees);
|
||||
console.log('MINING REWARD', this.calculateMiningReward(blockTemplate.height));
|
||||
transactions.unshift(coinbaseHash);
|
||||
|
||||
// Calculate merkle branch
|
||||
const merkleBranch = blockTemplate.transactions.slice(1).map((transaction) => transaction.hash);
|
||||
this.merkle_branch = this.buildMerkleBranch(merkleBranch, coinbaseHashBin);
|
||||
|
||||
const tree = new MerkleTree(transactions, this.sha256, { isBitcoinTree: true });
|
||||
const layers = tree.getLayers();
|
||||
|
||||
const branch = [];
|
||||
|
||||
for (const layer of layers) {
|
||||
branch.push(this.bufferToHex(layer[0]));
|
||||
}
|
||||
|
||||
//console.log(branch);
|
||||
|
||||
this.merkle_branch = branch;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private calculateMiningReward(blockHeight: number): number {
|
||||
const initialBlockReward = 50 * 1e8; // Initial block reward in satoshis (1 BTC = 100 million satoshis)
|
||||
const halvingInterval = 210000; // Number of blocks after which the reward halves
|
||||
|
||||
// Calculate the number of times the reward has been halved
|
||||
const halvingCount = Math.floor(blockHeight / halvingInterval);
|
||||
|
||||
// Calculate the current block reward in satoshis
|
||||
const currentReward = initialBlockReward / Math.pow(2, halvingCount);
|
||||
|
||||
return currentReward;
|
||||
}
|
||||
|
||||
private bufferToHex(buffer: Buffer): string {
|
||||
return buffer.toString('hex');
|
||||
}
|
||||
|
||||
private createCoinbaseTransaction(address: string, blockHeight: number): { coinb1: string, coinb2: string, coinbaseData: any } {
|
||||
// Generate coinbase script
|
||||
const coinbaseScript = `03${blockHeight.toString(16).padStart(8, '0')}54696d652026204865616c74682021`;
|
||||
|
||||
// Create coinbase transaction
|
||||
const version = '01000000';
|
||||
const inputs = '01' + '0000000000000000000000000000000000000000000000000000000000000000ffffffff';
|
||||
const coinbaseScriptSize = coinbaseScript.length / 2;
|
||||
const coinbaseScriptBytes = coinbaseScriptSize.toString(16).padStart(2, '0');
|
||||
const coinbaseTransaction = inputs + coinbaseScriptBytes + coinbaseScript + '00000000';
|
||||
|
||||
// Create output
|
||||
const outputCount = '01';
|
||||
const satoshis = '0f4240'; // 6.25 BTC in satoshis (1 BTC = 100,000,000 satoshis)
|
||||
const script = '1976a914' + address + '88ac'; // Change this to your desired output script
|
||||
const locktime = '00000000';
|
||||
|
||||
// Combine coinb1 and coinb2
|
||||
const coinb1 = version + coinbaseTransaction + outputCount + satoshis;
|
||||
const coinb2 = script + locktime;
|
||||
|
||||
const coinbaseData = version + coinbaseTransaction + outputCount + satoshis + script + locktime;
|
||||
|
||||
|
||||
return { coinb1, coinb2, coinbaseData };
|
||||
}
|
||||
|
||||
private sha256(data) {
|
||||
return crypto.createHash('sha256').update(data).digest()
|
||||
}
|
||||
|
||||
|
||||
public response() {
|
||||
|
||||
return {
|
||||
id: 0,
|
||||
method: eResponseMethod.MINING_NOTIFY,
|
||||
params: [
|
||||
'123',///this.job_id,
|
||||
this.job_id,
|
||||
this.prevhash,
|
||||
this.coinb1,
|
||||
this.coinb2,
|
||||
@@ -66,58 +137,6 @@ export class MiningJob {
|
||||
}
|
||||
|
||||
|
||||
private coinbasePrefix(coinbase: string, coinbaseHashBin: Buffer): string {
|
||||
const coinbaseData = Buffer.from(coinbase, 'hex');
|
||||
const coinbaseSize = Buffer.alloc(1, coinbaseData.length);
|
||||
const extraNoncePlaceholder = Buffer.alloc(4); // Assuming 4 bytes for extra nonce
|
||||
const concatenatedBuffer = Buffer.concat([coinbaseSize, coinbaseData, extraNoncePlaceholder]);
|
||||
const merkleRoot = this.doubleSHA(Buffer.concat([coinbaseHashBin, concatenatedBuffer]));
|
||||
|
||||
return merkleRoot.toString('hex');
|
||||
}
|
||||
|
||||
|
||||
private buildCoinbaseHashBin(coinbase: string): Buffer {
|
||||
const sha256 = crypto.createHash('sha256');
|
||||
const sha256Digest = sha256.update(Buffer.from(coinbase, 'hex')).digest();
|
||||
|
||||
const coinbaseHashSha256 = crypto.createHash('sha256');
|
||||
const coinbaseHash = coinbaseHashSha256.update(sha256Digest).digest();
|
||||
|
||||
return coinbaseHash;
|
||||
}
|
||||
|
||||
private buildMerkleBranch(merkleBranch: string[], coinbaseHashBin: Buffer): string[] {
|
||||
const merkleRoots: string[] = [];
|
||||
let merkleRoot = coinbaseHashBin;
|
||||
|
||||
for (const h of merkleBranch) {
|
||||
const concatenatedBuffer = Buffer.concat([merkleRoot, Buffer.from(h, 'hex')]);
|
||||
merkleRoot = this.doubleSHA(concatenatedBuffer);
|
||||
merkleRoots.push(merkleRoot.toString('hex'));
|
||||
}
|
||||
|
||||
return merkleRoots.slice(0, 1);
|
||||
}
|
||||
|
||||
// private buildMerkleRoot(merkleBranch: string[], coinbaseHashBin: Buffer): string {
|
||||
// let merkleRoot = coinbaseHashBin;
|
||||
// for (const h of merkleBranch) {
|
||||
// const concatenatedBuffer = Buffer.concat([merkleRoot, Buffer.from(h, 'hex')]);
|
||||
// merkleRoot = this.doubleSHA(concatenatedBuffer);
|
||||
// }
|
||||
// return merkleRoot.toString('hex');
|
||||
// }
|
||||
|
||||
private doubleSHA(data: Buffer): Buffer {
|
||||
const sha256 = crypto.createHash('sha256');
|
||||
const sha256Digest = sha256.update(data).digest();
|
||||
|
||||
const doubleSha256 = crypto.createHash('sha256');
|
||||
const doubleSha256Digest = doubleSha256.update(sha256Digest).digest();
|
||||
|
||||
return doubleSha256Digest;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user