UNPKG

bbqpool-stratum

Version:

High performance Stratum poolserver in Node.js. Optimized to build with GCC 10 and O3 / bugfixes

360 lines (323 loc) 20.4 kB
/* * * templates (Updated) * */ const utils = require('../main/utils'); const Algorithms = require('../main/algorithms'); const Template = require('../main/template'); const rpcData = { 'capabilities': [ 'proposal' ], 'version': 536870912, 'rules': [], 'vbavailable': {}, 'vbrequired': 0, 'previousblockhash': '9719aefb83ef6583bd4c808bbe7d49b629a60b375fc6e36bee039530bc7727e2', 'transactions': [{ 'data': '0100000001cba672d0bfdbcc441d171ef0723a191bf050932c6f8adc8a05b0cac2d1eb022f010000006c493046022100a23472410d8fd7eabf5c739bdbee5b6151ff31e10d5cb2b52abeebd5e9c06977022100c2cdde5c632eaaa1029dff2640158aaf9aab73fa021ed4a48b52b33ba416351801210212ee0e9c79a72d88db7af3fed18ae2b7ca48eaed995d9293ae0f94967a70cdf6ffffffff02905f0100000000001976a91482db4e03886ee1225fefaac3ee4f6738eb50df9188ac00f8a093000000001976a914c94f5142dd7e35f5645735788d0fe1343baf146288ac00000000', 'hash': '7c90a5087ac4d5b9361d47655812c89b4ad0dee6ecd5e08814d00ce7385aa317', 'depends': [], 'fee': 10000, 'sigops': 2 }], 'coinbaseaux': { 'flags': '' }, 'coinbasevalue': 5000000000, 'longpollid': '9719aefb83ef6583bd4c808bbe7d49b629a60b375fc6e36bee039530bc7727e22', 'target': '00000ffff0000000000000000000000000000000000000000000000000000000', 'mintime': 1614044921, 'mutable': [ 'time', 'transactions', 'prevblock' ], 'noncerange': '00000000ffffffff', 'sigoplimit': 20000, 'sizelimit': 1000000, 'curtime': 1614201893, 'bits': '1e0ffff0', 'height': 1, 'default_witness_commitment': '6a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9' }; const poolConfig = { 'settings': { 'testnet': false }, 'primary': { 'address': 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq', 'coin': { 'asicboost': true, 'rewards': {}, 'version': 1, 'algorithms': { 'mining': 'scrypt', 'block': 'scrypt', 'coinbase': 'sha256d', }, 'mainnet': { 'bech32': 'bc', 'bip32': { 'public': 0x0488b21e, 'private': 0x0488ade4, }, 'pubKeyHash': 0x00, 'scriptHash': 0x05, 'wif': 0x80, 'coin': 'btc', }, }, 'recipients': [], } }; const jobId = 1; const extraNonce = Buffer.from('f000000ff111111f', 'hex'); //////////////////////////////////////////////////////////////////////////////// describe('Test template functionality', () => { let poolConfigCopy, rpcDataCopy; beforeEach(() => { poolConfigCopy = JSON.parse(JSON.stringify(poolConfig)); rpcDataCopy = JSON.parse(JSON.stringify(rpcData)); }); test('Test current bignum implementation [1]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); expect(template.target.toNumber().toFixed(9)).toBe('1.1042625655198232e+71'); }); test('Test current bignum implementation [2]', () => { rpcDataCopy.target = null; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); expect(template.target.toNumber().toFixed(9)).toBe('1.1042625655198232e+71'); }); test('Test if target is not defined', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); delete rpcDataCopy.target; expect(template.target.toNumber().toFixed(9)).toBe('1.1042625655198232e+71'); expect(template.difficulty.toFixed(9)).toBe('0.000244141'); }); test('Test template difficulty calculation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); expect(template.difficulty.toFixed(9)).toBe('0.000244141'); }); test('Test merkle step calculation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const merkleSteps = template.merkle.steps; const merkleHashes = template.getMerkleHashes(merkleSteps); expect(merkleHashes.length).toBe(1); expect(merkleHashes[0]).toBe('17a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c'); }); test('Test merkle buffer calculation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const transactions = template.rpcData.transactions; const merkleBuffers = template.getTransactionBuffers(transactions); expect(merkleBuffers.length).toBe(2); expect(merkleBuffers[0]).toBe(null); expect(merkleBuffers[1]).toStrictEqual(Buffer.from('17a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c', 'hex')); }); // No Voting Data in the Testing template test('Test voting data', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); expect(template.getVoteData()).toStrictEqual(Buffer.from([])); }); test('Test generation transaction creation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const generation = template.createGeneration(poolConfigCopy, template.rpcData, extraNonce, null); expect(generation.length).toBe(2); expect(generation[0].slice(0, -5)).toStrictEqual(Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f5104', 'hex')); expect(generation[1]).toStrictEqual(Buffer.from('000000000200f2052a01000000160014e8df018c7e326cc253faac7e46cdc51e68542c420000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000', 'hex')); }); test('Test if txid is defined in the transaction', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); rpcDataCopy.transactions[0].txid = rpcDataCopy.transactions[0].hash; const merkleBuffers = template.getTransactionBuffers(rpcDataCopy.transactions); expect(merkleBuffers.length).toBe(2); expect(merkleBuffers[0]).toBe(null); expect(merkleBuffers[1]).toStrictEqual(Buffer.from('17a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c', 'hex')); }); test('Test if masternode voting data exists', () => { rpcDataCopy.masternode_payments = true; rpcDataCopy.votes = ['17a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c']; const templateVoting = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const templateVotes = templateVoting.getVoteData(); expect(templateVotes).toStrictEqual(Buffer.from('0117a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c', 'hex')); }); test('Test merkle creation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const merkle = template.createMerkle(template.rpcData, template.generation, poolConfigCopy); expect(merkle.steps.length).toBe(1); expect(merkle.steps[0]).toStrictEqual(Buffer.from('17a35a38e70cd01488e0d5ece6ded04a9bc8125865471d36b9d5c47a08a5907c', 'hex')); }); test('Test reversing of hashes', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); expect(template.previousblockhash).toBe('bc7727e2ee0395305fc6e36b29a60b37be7d49b6bd4c808b83ef65839719aefb'); }); test('Test coinbase serialization [1]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const extraNonce1 = Buffer.from('01', 'hex'); const extraNonce2 = Buffer.from('00', 'hex'); const coinbase = template.serializeCoinbase(extraNonce1, extraNonce2); expect(coinbase.slice(0, 44)).toStrictEqual(Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f5104', 'hex')); expect(coinbase.slice(49, 51)).toStrictEqual(Buffer.from('0100', 'hex')); expect(coinbase.slice(51)).toStrictEqual(Buffer.from('000000000200f2052a01000000160014e8df018c7e326cc253faac7e46cdc51e68542c420000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900000000', 'hex')); }); test('Test coinbase serialization [2]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const coinbase = Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac00000000', 'hex'); const coinbaseHash = template.coinbaseHasher(coinbase); expect(coinbaseHash).toStrictEqual(Buffer.from('afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b53031', 'hex')); }); test('Test merkle root generation', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const coinbase = Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac00000000', 'hex'); const coinbaseHash = template.coinbaseHasher(coinbase); const merkleRoot = utils.reverseBuffer(template.merkle.withFirst(coinbaseHash)).toString('hex'); expect(merkleRoot).toBe('0b8dcdd18969a859444b18f927f69202f5a8c4379b3ed5b3f7c1bd1f57e916d0'); }); test('Test header serialization [1]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const merkleRoot = '3130b519a5914d1eea42022f592802c2d6b3e08b71f101aca985ff0b1031d0af'; const time = '6036c54f'.toString('hex'); const nonce = 'fe1a0000'.toString('hex'); const headerBuffer = template.serializeHeader(merkleRoot, time, nonce, template.rpcData.version); expect(headerBuffer).toStrictEqual(Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae199700060003000008000701000100010000000908050000000001000301000000004fc53660f0ff0f1e00001afe', 'hex')); }); test('Test header serialization [2]', () => { const headerBuffer = Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe', 'hex'); const hashDigest = Algorithms[poolConfig.primary.coin.algorithms.mining].hash(poolConfig.primary.coin); const headerHash = hashDigest(headerBuffer, 1614202191); expect(headerHash).toStrictEqual(Buffer.from('3748391bfdaaa2a44424028a12fa508f94bb9ca879b2430a41cfa6e171040000', 'hex')); }); test('Test header serialization [3]', () => { poolConfigCopy.primary.coin.algorithms.mining = 'kawpow'; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const merkleRoot = '3130b519a5914d1eea42022f592802c2d6b3e08b71f101aca985ff0b1031d0af'; const time = '6036c54f'.toString('hex'); const nonce = 'fe1a0000'.toString('hex'); const headerBuffer = template.serializeHeader(merkleRoot, time, nonce, template.rpcData.version); expect(headerBuffer).toStrictEqual(Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae199700060003000008000701000100010000000908050000000001000301000000004fc53660f0ff0f1e01000000', 'hex')); }); test('Test block serialization [1]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const headerBuffer = Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe', 'hex'); const coinbase = Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac00000000', 'hex'); const templateHex = template.serializeBlock(headerBuffer, coinbase, null, null); expect(templateHex).toStrictEqual(Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac000000000100000001cba672d0bfdbcc441d171ef0723a191bf050932c6f8adc8a05b0cac2d1eb022f010000006c493046022100a23472410d8fd7eabf5c739bdbee5b6151ff31e10d5cb2b52abeebd5e9c06977022100c2cdde5c632eaaa1029dff2640158aaf9aab73fa021ed4a48b52b33ba416351801210212ee0e9c79a72d88db7af3fed18ae2b7ca48eaed995d9293ae0f94967a70cdf6ffffffff02905f0100000000001976a91482db4e03886ee1225fefaac3ee4f6738eb50df9188ac00f8a093000000001976a914c94f5142dd7e35f5645735788d0fe1343baf146288ac00000000', 'hex')); }); test('Test block serialization [2]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const headerBuffer = Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe', 'hex'); const templateHash = template.blockHasher(headerBuffer, 1614202191); expect(templateHash).toStrictEqual(Buffer.from('00000471e1a6cf410a43b279a89cbb948f50fa128a022444a4a2aafd1b394837', 'hex')); }); test('Test block serialization [3]', () => { poolConfigCopy.primary.coin.hybrid = true; poolConfigCopy.primary.pubkey = '020ba3ebc2f55152df5653bb7aba6548f0615d67b072379bdd19e72bc63c052c50'; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const headerBuffer = Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe', 'hex'); const coinbase = Buffer.from('01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac00000000', 'hex'); const templateHex = template.serializeBlock(headerBuffer, coinbase, null, null); expect(templateHex).toStrictEqual(Buffer.from('00000020e22777bc309503ee6be3c65f370ba629b6497dbe8b804cbd8365ef83fbae1997afd031100bff85a9ac01f1718be0b3d6c20228592f0242ea1e4d91a519b530314fc53660f0ff0f1e00001afe0201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff0100f2052a010000001976a914614ca2f0f4baccdd63f45a0e0e0ff7ffb88041fb88ac000000000100000001cba672d0bfdbcc441d171ef0723a191bf050932c6f8adc8a05b0cac2d1eb022f010000006c493046022100a23472410d8fd7eabf5c739bdbee5b6151ff31e10d5cb2b52abeebd5e9c06977022100c2cdde5c632eaaa1029dff2640158aaf9aab73fa021ed4a48b52b33ba416351801210212ee0e9c79a72d88db7af3fed18ae2b7ca48eaed995d9293ae0f94967a70cdf6ffffffff02905f0100000000001976a91482db4e03886ee1225fefaac3ee4f6738eb50df9188ac00f8a093000000001976a914c94f5142dd7e35f5645735788d0fe1343baf146288ac0000000000', 'hex')); }); test('Test block serialization [4]', () => { poolConfigCopy.primary.coin.algorithms.mining = 'kawpow'; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const headerBuffer = Buffer.from('63543d3913fe56e6720c5e61e8d208d05582875822628f483279a3e8d9c9a8b3', 'hex'); const mixHashBuffer = Buffer.from('89732e5ff8711c32558a308fc4b8ee77416038a70995670e3eb84cbdead2e337', 'hex'); const nonceBuffer = Buffer.from('88a23b0033eb959b', 'hex'); const templateHex = template.serializeBlock(headerBuffer, Buffer.from('', 'hex'), nonceBuffer, mixHashBuffer); expect(templateHex).toStrictEqual(Buffer.from('63543d3913fe56e6720c5e61e8d208d05582875822628f483279a3e8d9c9a8b388a23b0033eb959b37e3d2eabd4cb83e0e679509a738604177eeb8c48f308a55321c71f85f2e7389020100000001cba672d0bfdbcc441d171ef0723a191bf050932c6f8adc8a05b0cac2d1eb022f010000006c493046022100a23472410d8fd7eabf5c739bdbee5b6151ff31e10d5cb2b52abeebd5e9c06977022100c2cdde5c632eaaa1029dff2640158aaf9aab73fa021ed4a48b52b33ba416351801210212ee0e9c79a72d88db7af3fed18ae2b7ca48eaed995d9293ae0f94967a70cdf6ffffffff02905f0100000000001976a91482db4e03886ee1225fefaac3ee4f6738eb50df9188ac00f8a093000000001976a914c94f5142dd7e35f5645735788d0fe1343baf146288ac00000000', 'hex')); }); test('Test template submission', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const extraNonce1 = Buffer.from('01', 'hex'); const extraNonce2 = Buffer.from('00', 'hex'); const time = '6036c54f'.toString('hex'); const nonce = 'fe1a0000'.toString('hex'); const templateSubmitted1 = template.registerSubmit([extraNonce1, extraNonce2, time, nonce]); const templateSubmitted2 = template.registerSubmit([extraNonce1, extraNonce2, time, nonce]); expect(templateSubmitted1).toBe(true); expect(templateSubmitted2).toBe(false); }); test('Test current job parameters [1]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const jobParams = [ template.jobId, template.previousblockhash, template.generation[0].toString('hex'), template.generation[1].toString('hex'), template.getMerkleHashes(template.merkle.steps), utils.packInt32BE(template.rpcData.version).toString('hex'), template.rpcData.bits, utils.packInt32BE(template.rpcData.curtime).toString('hex'), true ]; const currentParams = template.getJobParams(null, true); expect(currentParams).toStrictEqual(jobParams); }); test('Test current job parameters [2]', () => { const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const jobParams = [ template.jobId, template.previousblockhash, template.generation[0].toString('hex'), template.generation[1].toString('hex'), template.getMerkleHashes(template.merkle.steps), utils.packInt32BE(template.rpcData.version).toString('hex'), template.rpcData.bits, utils.packInt32BE(template.rpcData.curtime).toString('hex'), true ]; template.jobParams = jobParams; const currentParams = template.getJobParams(null, true); expect(currentParams).toStrictEqual(jobParams); }); test('Test current job parameters [3]', () => { poolConfigCopy.primary.coin.algorithms.mining = 'kawpow'; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const jobParams = [ template.jobId, null, '0000000000000000000000000000000000000000000000000000000000000000', '00000fffee8a5814c68000000000000000000000000000000000000000000000', true, template.rpcData.height, template.rpcData.bits ]; const currentParams = template.getJobParams({ extraNonce1: '71000000' }, true); currentParams[1] = null; expect(currentParams).toStrictEqual(jobParams); }); test('Test current job parameters [4]', () => { rpcDataCopy.height = 1395113; poolConfigCopy.primary.coin.algorithms.mining = 'kawpow'; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); const jobParams = [ template.jobId, null, 'eeaf89f399eb3a301462d94af98532e0b70fa15bbf6b8af0a7deadaf5e11b68d', '00000fffee8a5814c68000000000000000000000000000000000000000000000', true, template.rpcData.height, template.rpcData.bits ]; const currentParams = template.getJobParams({ extraNonce1: '71000000' }, true); currentParams[1] = null; expect(currentParams).toStrictEqual(jobParams); }); test('Test current job parameters [5]', () => { rpcDataCopy.height = 1395113; poolConfigCopy.primary.coin.algorithms.mining = 'kawpow'; const client = {}; const template = new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); template.getJobParams(client, true); expect(client.extraNonce1).not.toBe(null); }); test('Test if configuration is not supported', () => { rpcDataCopy.coinbase_payload = 'example'; poolConfigCopy.auxiliary = { enabled: true }; expect(() => { new Template(poolConfigCopy, rpcDataCopy, jobId.toString(16), extraNonce, null); }).toThrow(Error); }); });