UNPKG

bbqpool-stratum

Version:

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

357 lines (322 loc) 8.88 kB
/* * * Utils (Updated) * */ // Import Required Modules const bchaddr = require('bchaddrjs'); const bignum = require('bignum'); const bitcoin = require('bbqpool-utxo-lib'); const crypto = require('crypto'); // Convert Address to Script exports.addressToScript = function(addr, network) { network = network || {}; if (network.coin === 'bch' && bchaddr.isCashAddress(addr)) { addr = bchaddr.toLegacyAddress(addr); return bitcoin.address.toOutputScript(addr, network); } else if (typeof network.coin !== 'undefined') { return bitcoin.address.toOutputScript(addr, network); } else { return Buffer.concat([Buffer.from([0x76, 0xa9, 0x14]), bitcoin.address.fromBase58Check(addr).hash, Buffer.from([0x88, 0xac])]); } }; // Convert Bits into Target Bignum exports.bignumFromBitsBuffer = function(bitsBuff) { const numBytes = bitsBuff.readUInt8(0); const bigBits = bignum.fromBuffer(bitsBuff.slice(1)); const target = bigBits.mul( bignum(2).pow( bignum(8).mul( numBytes - 3 ) ) ); return target; }; // Convert Bits into Target Bignum exports.bignumFromBitsHex = function(bitsString) { const bitsBuff = Buffer.from(bitsString, 'hex'); return exports.bignumFromBitsBuffer(bitsBuff); }; // Generate String Buffer from Fixed Length exports.commandStringBuffer = function(s) { const buff = Buffer.alloc(12); buff.fill(0); buff.write(s); return buff; }; // Generate Unique ExtraNonce for each Subscriber /* istanbul ignore next */ exports.extraNonceCounter = function(size) { return { size: size, next: function() { return(crypto.randomBytes(this.size).toString('hex')); } }; }; // Calculate Merkle Hash Position // https://github.com/p2pool/p2pool/blob/53c438bbada06b9d4a9a465bc13f7694a7a322b7/p2pool/bitcoin/data.py#L218 // https://stackoverflow.com/questions/8569113/why-1103515245-is-used-in-rand exports.getAuxMerklePosition = function(chain_id, size) { return (1103515245 * chain_id + 1103515245 * 12345 + 12345) % size; }; // Check if Input is Hex String exports.isHexString = function(s) { const check = String(s).toLowerCase(); if(check.length % 2) { return false; } for (let i = 0; i < check.length; i = i + 2) { const c = check[i] + check[i+1]; if (!exports.isHex(c)) return false; } return true; }; // Check if Input is Hex exports.isHex = function(c) { const a = parseInt(c,16); let b = a.toString(16).toLowerCase(); if(b.length % 2) { b = '0' + b; } if (b !== c) { return false; } return true; }; /*Unique extranonce per subscriber exports.ExtraNonceCounter = function(configInstanceId){ var maxBignum = bignum('ffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16); var makeExtraNonce = function(){ var extraNonce = bignum.rand(maxBignum).toString(16); if(extraNonce.length !== 56){ return makeExtraNonce(); }; return util.reverseBuffer(Buffer.from(extraNonce.toString(), 'hex')).toString('hex'); }; this.next = function(){ return makeExtraNonce() }; this.size = 28; //bytes }; */ // Generate Unique Job for each Template /* istanbul ignore next */ exports.jobCounter = function() { return { counter: 0, next: function() { this.counter += 1; if (this.counter % 0xffff === 0) { this.counter = 1; } return this.cur(); }, cur: function() { return this.counter.toString(16); } }; }; // Alloc/Write UInt16LE exports.packUInt16LE = function(num) { const buff = Buffer.alloc(2); buff.writeUInt16LE(num, 0); return buff; }; // Alloc/Write UInt16LE exports.packUInt16BE = function(num) { const buff = Buffer.alloc(2); buff.writeUInt16BE(num, 0); return buff; }; // Alloc/Write UInt32LE exports.packUInt32LE = function(num) { const buff = Buffer.alloc(4); buff.writeUInt32LE(num, 0); return buff; }; // Alloc/Write UInt32BE exports.packUInt32BE = function(num) { const buff = Buffer.alloc(4); buff.writeUInt32BE(num, 0); return buff; }; // Alloc/Write Int64LE exports.packUInt64LE = function(num) { const buff = Buffer.alloc(8); buff.writeUInt32LE(num % Math.pow(2, 32), 0); buff.writeUInt32LE(Math.floor(num / Math.pow(2, 32)), 4); return buff; }; // Alloc/Write Int64LE exports.packUInt64BE = function(num) { const buff = Buffer.alloc(8); buff.writeUInt32BE(Math.floor(num / Math.pow(2, 32)), 0); buff.writeUInt32BE(num % Math.pow(2, 32), 4); return buff; }; // Alloc/Write Int32LE exports.packInt32LE = function(num) { const buff = Buffer.alloc(4); buff.writeInt32LE(num, 0); return buff; }; // Alloc/Write Int32BE exports.packInt32BE = function(num) { const buff = Buffer.alloc(4); buff.writeInt32BE(num, 0); return buff; }; // Convert PubKey to Script exports.pubkeyToScript = function(key){ if (key.length !== 66) { throw new Error('Invalid pubkey: ' + key); } const pubKey = Buffer.concat([Buffer.from([0x21]), Buffer.alloc(33), Buffer.from([0xac])]); const bufferKey = Buffer.from(key, 'hex'); bufferKey.copy(pubKey, 1); return pubKey; }; // Range Function exports.range = function(start, stop, step) { if (typeof step === 'undefined') { step = 1; } if (typeof stop === 'undefined') { stop = start; start = 0; } if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) { return []; } const result = []; for (let i = start; step > 0 ? i < stop : i > stop; i += step) { result.push(i); } return result; }; // Reverse Input Buffer exports.reverseBuffer = function(buff) { const reversed = Buffer.alloc(buff.length); for (let i = buff.length - 1; i >= 0; i--) { reversed[buff.length - i - 1] = buff[i]; } return reversed; }; // Reverse Byte Order of Input Buffer exports.reverseByteOrder = function(buff) { for (let i = 0; i < 8; i += 1) { buff.writeUInt32LE(buff.readUInt32BE(i * 4), i * 4); } return exports.reverseBuffer(buff); }; // Reverse Input Buffer + Hex String exports.reverseHex = function(hex) { return exports.reverseBuffer(Buffer.from(hex, 'hex')).toString('hex'); }; // Serialize Height/Date Input exports.serializeNumber = function(n) { if (n >= 1 && n <= 16) { return Buffer.from([0x50 + n]); } let l = 1; const buff = Buffer.alloc(9); while (n > 0x7f) { buff.writeUInt8(n & 0xff, l++); n >>= 8; } buff.writeUInt8(l, 0); buff.writeUInt8(n, l++); return buff.slice(0, l); }; // Serialize Strings used for Signature /* istanbul ignore next */ exports.serializeString = function(s) { if (s.length < 253) { return Buffer.concat([ Buffer.from([s.length]), Buffer.from(s) ]); } else if (s.length < 0x10000) { return Buffer.concat([ Buffer.from([253]), exports.packUInt16LE(s.length), Buffer.from(s) ]); } else if (s.length < 0x100000000) { return Buffer.concat([ Buffer.from([254]), exports.packUInt32LE(s.length), Buffer.from(s) ]); } else { return Buffer.concat([ Buffer.from([255]), exports.packUInt16LE(s.length), Buffer.from(s) ]); } }; // Hash Input w/ Sha256 exports.sha256 = function(buffer) { const hash1 = crypto.createHash('sha256'); hash1.update(buffer); return hash1.digest(); }; // Hash Input w/ Sha256d exports.sha256d = function(buffer) { return exports.sha256(exports.sha256(buffer)); }; // Increment Count for Each Subscription /* istanbul ignore next */ exports.subscriptionCounter = function() { let count = 0; const padding = 'deadbeefcafebabe'; return { next: function() { count += 1; if (Number.MAX_VALUE === count) count = 0; return padding + exports.packUInt64LE(count).toString('hex'); } }; }; // Truncate to Fixed Decimal Places exports.toFixed = function(num, len) { return parseFloat(num.toFixed(len)); }; // Generate Reverse Buffer from Input Hash exports.uint256BufferFromHash = function(hex) { let fromHex = Buffer.from(hex, 'hex'); if (fromHex.length != 32) { const empty = Buffer.alloc(32); empty.fill(0); fromHex.copy(empty); fromHex = empty; } return exports.reverseBuffer(fromHex); }; // Generate VarInt Buffer exports.varIntBuffer = function(n) { if (n < 0xfd) { return Buffer.from([n]); } else if (n <= 0xffff) { const buff = Buffer.alloc(3); buff[0] = 0xfd; exports.packUInt16LE(n).copy(buff, 1); return buff; } else if (n <= 0xffffffff) { const buff = Buffer.alloc(5); buff[0] = 0xfe; exports.packUInt32LE(n).copy(buff, 1); return buff; } else { const buff = Buffer.alloc(9); buff[0] = 0xff; exports.packUInt64LE(n).copy(buff, 1); return buff; } }; // Generate VarString Buffer exports.varStringBuffer = function(string) { const strBuff = Buffer.from(string); return Buffer.concat([exports.varIntBuffer(strBuff.length), strBuff]); };