diff --git a/package-lock.json b/package-lock.json index 4c1392c..808ec59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "@nestjs/core": "^9.0.0", "@nestjs/platform-fastify": "^9.4.2", "big.js": "^6.2.1", + "bitcoinjs-lib": "^6.1.3", + "bs58": "^5.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "merkletreejs": "^0.3.10", @@ -1692,6 +1694,17 @@ } } }, + "node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2830,12 +2843,9 @@ "dev": true }, "node_modules/base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "dependencies": { - "safe-buffer": "^5.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -2864,6 +2874,11 @@ "tweetnacl": "^0.14.3" } }, + "node_modules/bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, "node_modules/big.js": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", @@ -2893,6 +2908,39 @@ "node": ">=8" } }, + "node_modules/bip174": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.0.tgz", + "integrity": "sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA==", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz", + "integrity": "sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.0", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bitcoinjs-lib/node_modules/bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "dependencies": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -3037,11 +3085,11 @@ } }, "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", "dependencies": { - "base-x": "^3.0.2" + "base-x": "^4.0.0" } }, "node_modules/bs58check": { @@ -3054,6 +3102,22 @@ "safe-buffer": "^5.1.2" } }, + "node_modules/bs58check/node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58check/node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -8501,6 +8565,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, "node_modules/typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", @@ -8625,6 +8694,14 @@ "node": ">= 0.10" } }, + "node_modules/varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -10147,6 +10224,11 @@ "tslib": "2.5.2" } }, + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -11096,12 +11178,9 @@ "dev": true }, "base-x": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", - "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", - "requires": { - "safe-buffer": "^5.0.1" - } + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==" }, "base64-js": { "version": "1.5.1", @@ -11116,6 +11195,11 @@ "tweetnacl": "^0.14.3" } }, + "bech32": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", + "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" + }, "big.js": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", @@ -11132,6 +11216,35 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bip174": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bip174/-/bip174-2.1.0.tgz", + "integrity": "sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA==" + }, + "bitcoinjs-lib": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/bitcoinjs-lib/-/bitcoinjs-lib-6.1.3.tgz", + "integrity": "sha512-TYXs/Qf+GNk2nnsB9HrXWqzFuEgCg0Gx+v3UW3B8VuceFHXVvhT+7hRnTSvwkX0i8rz2rtujeU6gFaDcFqYFDw==", + "requires": { + "@noble/hashes": "^1.2.0", + "bech32": "^2.0.0", + "bip174": "^2.1.0", + "bs58check": "^3.0.1", + "typeforce": "^1.11.3", + "varuint-bitcoin": "^1.1.2" + }, + "dependencies": { + "bs58check": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-3.0.1.tgz", + "integrity": "sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==", + "requires": { + "@noble/hashes": "^1.2.0", + "bs58": "^5.0.0" + } + } + } + }, "bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -11235,11 +11348,11 @@ } }, "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", + "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", "requires": { - "base-x": "^3.0.2" + "base-x": "^4.0.0" } }, "bs58check": { @@ -11250,6 +11363,24 @@ "bs58": "^4.0.0", "create-hash": "^1.1.0", "safe-buffer": "^5.1.2" + }, + "dependencies": { + "base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "requires": { + "base-x": "^3.0.2" + } + } } }, "bser": { @@ -15309,6 +15440,11 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, + "typeforce": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", + "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==" + }, "typescript": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", @@ -15392,6 +15528,14 @@ "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==" }, + "varuint-bitcoin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz", + "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/package.json b/package.json index aab6ca1..8731ba5 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,8 @@ "@nestjs/core": "^9.0.0", "@nestjs/platform-fastify": "^9.4.2", "big.js": "^6.2.1", + "bitcoinjs-lib": "^6.1.3", + "bs58": "^5.0.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", "merkletreejs": "^0.3.10", @@ -73,4 +75,4 @@ "coverageDirectory": "../coverage", "testEnvironment": "node" } -} \ No newline at end of file +} diff --git a/src/models/MiningJob.ts b/src/models/MiningJob.ts index 119ae13..a067657 100644 --- a/src/models/MiningJob.ts +++ b/src/models/MiningJob.ts @@ -1,3 +1,4 @@ +import * as bitcoinjs from 'bitcoinjs-lib'; import * as crypto from 'crypto'; import { MerkleTree } from 'merkletreejs'; @@ -32,7 +33,7 @@ export class MiningJob { public tree: MerkleTree; - constructor(id: string, blockTemplate: IBlockTemplate, _cleanJobs: boolean) { + constructor(id: string, public blockTemplate: IBlockTemplate, _cleanJobs: boolean) { //console.log(blockTemplate); @@ -53,7 +54,7 @@ export class MiningJob { //console.log('TRANSACTION FEES', transactionFees); //console.log('MINING REWARD', miningReward); - const { coinbasePart1, coinbasePart2 } = this.createCoinbaseTransaction([{ address: '', percentage: 100 }], blockTemplate.height, transactionFees + miningReward); + const { coinbasePart1, coinbasePart2 } = this.createCoinbaseTransaction([{ address: 'bc1qsa5kr5s74wss7pcpmnrc7e3y5nrgyfcdzpwakk', percentage: 100 }], blockTemplate.height, transactionFees + miningReward); this.coinb1 = coinbasePart1; this.coinb2 = coinbasePart2; @@ -100,39 +101,48 @@ export class MiningJob { private createCoinbaseTransaction(addresses: AddressObject[], blockHeight: number, reward: number): { coinbasePart1: string, coinbasePart2: string } { // Generate coinbase script - const coinbaseScript = `03${blockHeight.toString(16).padStart(8, '0')}54696d652026204865616c74682021`; + const blockHeightScript = `03${blockHeight.toString(16).padStart(8, '0')}`; + const inputScript = `54696d652026204865616c74682021`; // Create coinbase transaction + const endOfInput = 'ffffffff'; + const lockTime = '00000000'; const version = '01000000'; const inputCount = '01'; - const inputs = '0000000000000000000000000000000000000000000000000000000000000000ffffffff'; - const coinbaseScriptSize = coinbaseScript.length / 2; - const coinbaseScriptBytes = coinbaseScriptSize.toString(16).padStart(2, '0'); - const coinbaseTransaction = inputCount + inputs + coinbaseScriptBytes + coinbaseScript + '00000000'; + const fakeCoinbaseInput = '0000000000000000000000000000000000000000000000000000000000000000'; - // Create outputs - const outputCount = addresses.length; - const outputCountHex = outputCount.toString(16).padStart(2, '0'); + const inputScriptBytes = ((blockHeightScript.length + inputScript.length + lockTime.length) / 2).toString(16).padStart(2, '0'); + + + const inputTransaction = inputCount + fakeCoinbaseInput + endOfInput + inputScriptBytes + blockHeightScript + inputScript + lockTime; + + + + //let remainingPayout = reward; - let remainingPayout = reward; const outputs = addresses .map((addressObj) => { const percentage = addressObj.percentage / 100; - const satoshis = Math.floor(reward * percentage).toString(16).padStart(16, '0'); - remainingPayout -= parseInt(satoshis, 16); - const script = '1976a914' + Buffer.from(addressObj.address, 'hex').toString('hex') + '88ac'; // Convert address to hex - return satoshis + script; + const satoshis = Math.floor(reward * percentage); + const satoshiBuff = Buffer.alloc(4); + satoshiBuff.writeUInt32LE(satoshis); + const littleEndianSatoshis = satoshiBuff.toString('hex').padEnd(16, '0'); + + const script = bitcoinjs.payments.p2wpkh({ address: addressObj.address }).output.toString('hex') // Convert address to hex + const scriptBytes = (script.length / 2).toString(16).padStart(2, '0'); + return littleEndianSatoshis + scriptBytes + script; }) .join(''); - // Distribute any remaining satoshis to the first address - const firstAddressSatoshis = (parseInt(outputs.substring(0, 16), 16) + remainingPayout).toString(16).padStart(16, '0'); - const firstAddressOutput = firstAddressSatoshis + outputs.substring(16); - const modifiedOutputs = firstAddressOutput + outputs.slice(16); + // // Distribute any remaining satoshis to the first address + // const firstAddressSatoshis = (parseInt(outputs.substring(0, 16), 16) + remainingPayout).toString(16).padStart(16, '0'); // Combine coinbasePart1 and coinbasePart2 - const coinbasePart1 = version + coinbaseTransaction + outputCountHex; - const coinbasePart2 = modifiedOutputs + '00000000'; + // Create outputs + const outputCountHex = addresses.length.toString(16).padStart(2, '0'); + + const coinbasePart1 = version + inputTransaction + endOfInput + outputCountHex; + const coinbasePart2 = outputs + lockTime; return { coinbasePart1, coinbasePart2 }; } diff --git a/src/models/stratum-messages/SubscriptionMessage.ts b/src/models/stratum-messages/SubscriptionMessage.ts index 7be3499..2303d5e 100644 --- a/src/models/stratum-messages/SubscriptionMessage.ts +++ b/src/models/stratum-messages/SubscriptionMessage.ts @@ -25,7 +25,7 @@ export class SubscriptionMessage extends StratumBaseMessage { // ['mining.notify', '64d8c004'] ], //subscription details clientId, //Extranonce1 - Hex-encoded, per-connection unique string which will be used for coinbase serialization later. Keep it safe! - 8 //Extranonce2_size - Represents expected length of extranonce2 which will be generated by the miner. + 4 //Extranonce2_size - Represents expected length of extranonce2 which will be generated by the miner. ] }