diff --git a/README.md b/README.md index 747cb70..1f95100 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,6 @@ Listen to pool events ip: '71.33.19.37', //ip address of client worker: 'matt.worker1', //stratum worker name height: 443795, //block height - poolReward: 4900000000, //the number of satoshis sent to the configured pool address blockReward: 5000000000, //the number of satoshis received as payment for solving this block difficulty: 64, //stratum worker difficulty shareDiff: 78, //actual difficulty of the share diff --git a/lib/blockTemplate.js b/lib/blockTemplate.js index af157fa..886cdd1 100644 --- a/lib/blockTemplate.js +++ b/lib/blockTemplate.js @@ -28,26 +28,23 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, pool return [null].concat(txHashes); } + function getVoteData(){ + if (!rpcData.masternode_payments) return new Buffer([]); + + return Buffer.concat( + [util.varIntBuffer(rpcData.votes.length)].concat( + rpcData.votes.map(function (vt) { + return new Buffer(vt, 'hex'); + }) + ) + ); + } //public members this.rpcData = rpcData; this.jobId = jobId; - this.poolReward = (function(){ - var pReward = rpcData.coinbasevalue; - for (var i = 0; i < recipients.length; i++){ - var recipientReward = Math.floor(recipients[i].percent * rpcData.coinbasevalue); - pReward -= recipientReward; - } - return pReward; - })(); - - - //Use the 'target' field if available, but some daemons only return the 'bits' field - /*this.target = rpcData.target ? - bignum.fromBuffer(new Buffer(rpcData.target, 'hex')) : - util.bignumFromBits(rpcData.bits);*/ this.target = rpcData.target ? bignum(rpcData.target, 16) : @@ -55,6 +52,10 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, pool this.difficulty = parseFloat((diff1 / this.target.toNumber()).toFixed(9)); + + + + this.prevHashReversed = util.reverseByteOrder(new Buffer(rpcData.previousblockhash, 'hex')).toString('hex'); this.transactionData = Buffer.concat(rpcData.transactions.map(function(tx){ return new Buffer(tx.data, 'hex'); @@ -98,9 +99,13 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, pool this.serializeBlock = function(header, coinbase){ return Buffer.concat([ header, + util.varIntBuffer(this.rpcData.transactions.length + 1), coinbase, this.transactionData, + + getVoteData(), + //POS coins require a zero byte appended to block which the daemon replaces with the signature new Buffer(reward === 'POS' ? [0] : []) ]); diff --git a/lib/daemon.js b/lib/daemon.js index 58ec495..7e5ace1 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -13,16 +13,19 @@ var async = require('async'); * - 'password': password for the rpc interface of the coin **/ -function DaemonInterface(options){ +function DaemonInterface(daemons, logger){ //private members var _this = this; - this.options = options; + logger = logger || function(severity, message){ + console.log(severity + ': ' + message); + }; + var instances = (function(){ - for (var i = 0; i < options.length; i++) - options[i]['index'] = i; - return options; + for (var i = 0; i < daemons.length; i++) + daemons[i]['index'] = i; + return daemons; })(); @@ -58,33 +61,28 @@ function DaemonInterface(options){ var parseJson = function(res, data){ var dataJson; - var parsingError; + + if (res.statusCode === 401){ + logger('error', 'Unauthorized RPC access - invalid RPC username or password'); + return; + } try{ dataJson = JSON.parse(data); - } catch(e){ - if (res.statusCode === 401){ - parsingError = 'unauthorized'; - _this.emit('error', 'Invalid RPC username or password'); - } - else if (data.indexOf(':-nan,') !== -1){ - data = data.replace(/:-nan,/g, ":0,"); + if (data.indexOf(':-nan') !== -1){ + data = data.replace(/:-nan,/g, ":0"); parseJson(res, data); return; } - else{ - parsingError = e; - _this.emit('error', 'could not parse rpc data with request of: ' + jsonData + - ' on instance ' + instance.index + ' data: ' + data + ' Error ' + JSON.stringify(parsingError)); - } + logger('error', 'Could not parse rpc data from daemon instance ' + instance.index + + '\nRequest Data: ' + jsonData + + '\nReponse Data: ' + data); + } - if (typeof(dataJson) !== 'undefined'){ + if (dataJson) callback(dataJson.error, dataJson, data); - } - else - callback(parsingError); }; var req = http.request(options, function(res) { diff --git a/lib/jobManager.js b/lib/jobManager.js index 173101c..af27bc0 100644 --- a/lib/jobManager.js +++ b/lib/jobManager.js @@ -286,7 +286,6 @@ var JobManager = module.exports = function JobManager(options){ worker: workerName, height: job.rpcData.height, blockReward: job.rpcData.coinbasevalue, - poolReward: job.poolReward, difficulty: difficulty, shareDiff: shareDiff.toFixed(8), blockDiff : blockDiffAdjusted, diff --git a/lib/pool.js b/lib/pool.js index 4e9b66a..50efab7 100644 --- a/lib/pool.js +++ b/lib/pool.js @@ -8,6 +8,10 @@ var stratum = require('./stratum.js'); var jobManager = require('./jobManager.js'); var util = require('./util.js'); +process.on('uncaughtException', function(err) { + console.log(err.stack); + throw err; +}); var pool = module.exports = function pool(options, authorizeFn){ @@ -235,17 +239,21 @@ var pool = module.exports = function pool(options, authorizeFn){ _this.daemon.cmd(rpcCommand, rpcArgs, function(results){ - results.forEach(function(result){ - if (result.error) + for (var i = 0; i < results.length; i++){ + var result = results[i]; + if (result.error) { emitErrorLog('rpc error with daemon instance ' + - result.instance.index + ' when submitting block with ' + rpcCommand + ' ' + - JSON.stringify(result.error) + result.instance.index + ' when submitting block with ' + rpcCommand + ' ' + + JSON.stringify(result.error) ); - else - emitLog('Submitted Block using ' + rpcCommand + ' to daemon instance ' + - result.instance.index - ); - }); + return; + } + else if (result.response === 'rejected') { + emitErrorLog('Daemon instance ' + result.instance.index + ' rejected a supposedly valid block'); + return; + } + } + emitLog('Submitted Block using ' + rpcCommand + ' successfully to daemon instance(s)'); callback(); } ); @@ -338,7 +346,9 @@ var pool = module.exports = function pool(options, authorizeFn){ return; } - _this.daemon = new daemon.interface(options.daemons); + _this.daemon = new daemon.interface(options.daemons, function(severity, message){ + _this.emit('log', severity , message); + }); _this.daemon.once('online', function(){ finishedCallback(); diff --git a/lib/transactions.js b/lib/transactions.js index 62a405b..6257863 100644 --- a/lib/transactions.js +++ b/lib/transactions.js @@ -123,18 +123,35 @@ For some (probably outdated and incorrect) documentation about whats kinda going see: https://en.bitcoin.it/wiki/Protocol_specification#tx */ -var generateOutputTransactions = function(poolRecipient, recipients, reward){ +var generateOutputTransactions = function(poolRecipient, recipients, rpcData){ - var totalOutputs = 1; + var reward = rpcData.coinbasevalue; + var rewardToPool = reward; var txOutputBuffers = []; - var totalToRecipients = 0; + + + if (rpcData.payee) { + var payeeReward = Math.ceil(reward / 10); + + reward -= payeeReward; + rewardToPool -= payeeReward; + + var payeeScript = util.addressToScript(rpcData.payee); + txOutputBuffers.push(Buffer.concat([ + util.packInt64LE(payeeReward), + util.varIntBuffer(payeeScript.length), + payeeScript + ])); + } + + for (var i = 0; i < recipients.length; i++){ var recipientReward = Math.floor(recipients[i].percent * reward); - totalToRecipients += recipientReward; - totalOutputs++; + rewardToPool -= recipientReward; + txOutputBuffers.push(Buffer.concat([ util.packInt64LE(recipientReward), util.varIntBuffer(recipients[i].script.length), @@ -142,7 +159,6 @@ var generateOutputTransactions = function(poolRecipient, recipients, reward){ ])); } - var rewardToPool = reward - totalToRecipients; txOutputBuffers.push(Buffer.concat([ util.packInt64LE(rewardToPool), @@ -151,7 +167,7 @@ var generateOutputTransactions = function(poolRecipient, recipients, reward){ ])); return Buffer.concat([ - util.varIntBuffer(totalOutputs), + util.varIntBuffer(txOutputBuffers.length), Buffer.concat(txOutputBuffers) ]); @@ -207,13 +223,16 @@ exports.CreateGeneration = function(rpcData, publicKey, extraNoncePlaceholder, r a valid share and/or block. */ + + var outputTransactions = generateOutputTransactions(publicKey, recipients, rpcData); + var p2 = Buffer.concat([ scriptSigPart2, util.packUInt32LE(txInSequence), //end transaction input //transaction output - generateOutputTransactions(publicKey, recipients, rpcData.coinbasevalue), + outputTransactions, //end transaction ouput util.packUInt32LE(txLockTime),