Added fixes for POS. And when getblocktemplate fails because its out of sync then it shows syncing progress :)

This commit is contained in:
Matt 2014-03-23 22:22:01 -06:00
parent 32dc22944e
commit ffc01054c6
4 changed files with 156 additions and 83 deletions

View File

@ -83,7 +83,6 @@ var pool = Stratum.createPool({
name: "Dogecoin",
symbol: "doge",
algorithm: "scrypt", //or "sha256", "scrypt-jane", "quark", "x11"
reward: "POW", //or "POS"
txMessages: false //or true
},

View File

@ -67,6 +67,11 @@ function DaemonInterface(options){
var dataJson;
var parsingError;
//if (data.indexOf(':-nan,') !== -1){
// data = data.replace(/:-nan,/g, ":0,")
//}
try{
dataJson = JSON.parse(data);
@ -79,7 +84,7 @@ function DaemonInterface(options){
else{
parsingError = e;
_this.emit('error', 'could not parse rpc data with request of: ' + jsonData +
' on instance ' + instance.index + ' data: ' + data);
' on instance ' + instance.index + ' data: ' + data + ' Error ' + JSON.stringify(parsingError));
}
}
if (typeof(dataJson) !== 'undefined'){

View File

@ -50,6 +50,7 @@ var JobCounter = function(){
**/
var JobManager = module.exports = function JobManager(options){
//private members
var _this = this;
@ -58,7 +59,7 @@ var JobManager = module.exports = function JobManager(options){
//Which number to use as dividend when converting difficulty to target
var diffDividend = bignum((function(){
switch(options.algorithm){
switch(options.coin.algorithm){
case 'sha256':
case 'skein':
return '00000000ffff0000000000000000000000000000000000000000000000000000';
@ -76,7 +77,7 @@ var JobManager = module.exports = function JobManager(options){
//On initialization lets figure out which hashing algorithm to use
var hashDigest = (function(){
switch(options.algorithm){
switch(options.coin.algorithm){
case 'sha256':
return function(){
return util.doublesha.apply(this, arguments);
@ -119,6 +120,10 @@ var JobManager = module.exports = function JobManager(options){
//https://github.com/Prydie/maxcoin-hash-python
//https://github.com/ahmedbodi/stratum-mining-maxcoin/blob/master/lib/template_registry.py
}
default:
return function(){
console.log('Hashing algorithm ' + options.coin.algorithm + ' not supported');
}
}
})();
@ -167,8 +172,8 @@ var JobManager = module.exports = function JobManager(options){
rpcData,
publicKey,
_this.extraNoncePlaceholder,
options.reward,
options.txMessages
options.coin.reward,
options.coin.txMessages
);
this.currentJob = tmpBlockTemplate;

View File

@ -39,14 +39,7 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('Starting pool for ' + options.coin.name + ' [' + options.coin.symbol.toUpperCase() + ']');
SetupJobManager();
SetupVarDiff();
SetupDaemonInterface(function (err, newDaemon) {
if (!err) {
_this.daemon = newDaemon;
SetupBlockPolling();
StartStratumServer();
SetupPeer();
}
});
SetupDaemonInterface();
SetupApi();
};
@ -76,6 +69,7 @@ var pool = module.exports = function pool(options, authorizeFn){
}
function SetupVarDiff(){
_this.varDiff = {};
Object.keys(options.ports).forEach(function(port) {
if (options.ports[port].varDiff)
_this.setVarDiff(port, new varDiff(port, options.ports[port].varDiff));
@ -120,13 +114,9 @@ var pool = module.exports = function pool(options, authorizeFn){
function SetupJobManager(){
_this.jobManager = new jobManager({
address : options.address,
algorithm : options.coin.algorithm,
reward : options.coin.reward,
txMessages: options.coin.txMessages,
txRefreshInterval: options.txRefreshInterval
});
_this.jobManager = new jobManager(options);
_this.jobManager.on('newBlock', function(blockTemplate){
//Check if stratumServer has been initialized yet
if ( typeof(_this.stratumServer ) !== 'undefined') {
@ -170,12 +160,15 @@ var pool = module.exports = function pool(options, authorizeFn){
}
function SetupDaemonInterface(cback){
var newDaemon = new daemon.interface(options.daemons);
newDaemon.once('online', function(){
function SetupDaemonInterface(){
if (!_this.daemon) _this.daemon = new daemon.interface(options.daemons);
_this.daemon.once('online', function(){
async.parallel({
addressInfo: function(callback){
newDaemon.cmd('validateaddress', [options.address], function(results){
_this.daemon.cmd('validateaddress', [options.address], function(results){
//Make sure address is valid with each daemon
var allValid = results.every(function(result){
@ -197,8 +190,9 @@ var pool = module.exports = function pool(options, authorizeFn){
});
},
miningInfo: function(callback){
newDaemon.cmd('getmininginfo', [], function(results){
info: function(callback){
_this.daemon.cmd('getinfo', [], function(results){
// Print which network each daemon is running on
@ -212,9 +206,6 @@ var pool = module.exports = function pool(options, authorizeFn){
return false;
}
var network = result.response.testnet ? 'testnet' : 'live blockchain';
emitLog('Daemon instance ' + result.instance.index + ' is running on ' + network);
if (typeof isTestnet === 'undefined'){
isTestnet = result.response.testnet;
return true;
@ -242,17 +233,41 @@ var pool = module.exports = function pool(options, authorizeFn){
});
},
rewardType: function(callback){
_this.daemon.cmd('getdifficulty', [], function(results){
var isPos = results.every(function(result){
if (result.error){
emitErrorLog('getinfo on init failed with daemon instance ' +
result.instance.index + ', error ' + JSON.stringify(result.error)
);
return false;
}
return isNaN(result.response) && 'proof-of-stake' in result.response;
});
options.coin.reward = isPos ? 'POS' : 'POW'
callback(null);
});
},
submitMethod: function(callback){
/* This checks to see whether the daemon uses submitblock
or getblocktemplate for submitting new blocks */
newDaemon.cmd('submitblock', [], function(results){
_this.daemon.cmd('submitblock', [], function(results){
var couldNotDetectMethod = results.every(function(result){
if (result.error && result.error.message === 'Method not found'){
callback(null, false);
options.hasSubmitMethod = false;
callback(null);
return false;
}
else if (result.error && result.error.code === -1){
callback(null, true);
options.hasSubmitMethod = true;
callback(null);
return false;
}
else
@ -267,76 +282,127 @@ var pool = module.exports = function pool(options, authorizeFn){
}, function(err, results){
if (err){
emitErrorLog('Could not start pool, ' + JSON.stringify(err));
cback(err);
return;
}
emitLog('Connected to daemon via RPC');
options.hasSubmitMethod = results.submitMethod;
if (options.coin.reward === 'POS' && typeof(results.addressInfo.pubkey) == 'undefined') {
// address provided is not of the wallet.
emitErrorLog('The address provided is not from the daemon wallet.');
cback(err);
return;
} else {
publicKeyBuffer = options.coin.reward === 'POW' ?
util.script_to_address(results.addressInfo.address) :
util.script_to_pubkey(results.addressInfo.pubkey);
//var networkDifficulty = Math.round(results.miningInfo.difficulty * 65536);
GetBlockTemplate(newDaemon, function(error, result){
if (error) {
emitErrorLog('Error with getblocktemplate on initializing');
cback(error);
} else {
var networkDifficulty = _this.jobManager.currentJob.difficulty;
emitLog('Current block height at ' + results.miningInfo.blocks +
' with block difficulty of ' + networkDifficulty);
Object.keys(options.ports).forEach(function(port){
var portDiff = options.ports[port].diff;
if (portDiff > networkDifficulty)
emitWarningLog('diff of ' + portDiff + ' on port ' + port +
' was set higher than network difficulty of ' + networkDifficulty);
});
cback(null, newDaemon); // finish!
}
});
}
publicKeyBuffer = options.coin.reward === 'POW' ?
util.script_to_address(results.addressInfo.address) :
util.script_to_pubkey(results.addressInfo.pubkey);
var networkDifficulty = Math.round(results.info.difficulty * 65536);
var network = results.info.testnet ? 'testnet' : 'live blockchain';
emitLog('Connected to ' + network +
'; detected ' + options.coin.reward +
' reward type; block height of ' + results.info.blocks +
'; difficulty of ' + networkDifficulty);
GetBlockTemplate(function(error, result){
if (error) {
if (error.code === -10){
emitErrorLog('Daemon is still syncing with network (download blocks)');
WaitForSync(results.info.blocks);
}
else
emitErrorLog('Error with getblocktemplate on initializing');
} else {
Object.keys(options.ports).forEach(function(port){
var portDiff = options.ports[port].diff;
if (portDiff > networkDifficulty)
emitWarningLog('Pool difficulty of ' + portDiff + ' on port ' + port +
' was set higher than network difficulty of ' + networkDifficulty);
});
SetupBlockPolling();
StartStratumServer();
SetupPeer();
}
});
});
}).on('connectionFailed', function(error){
emitErrorLog('Failed to connect daemon(s): ' + JSON.stringify(error));
}).on('error', function(message){
emitErrorLog(message);
});
newDaemon.init();
_this.daemon.init();
}
function WaitForSync(currentBlocks){
var generateProgress = function(currentBlocks){
//get list of peers and their highest block height to compare to ours
_this.daemon.cmd('getpeerinfo', [], function(results){
var peers = results[0].response;
var totalBlocks = peers.sort(function(a, b){
return b.startingheight - a.startingheight;
})[0].startingheight;
var percent = (currentBlocks / totalBlocks * 100).toFixed(2);
//Only let the first fork show synced status or the log wil look flooded with it
if (process.env.forkId === '0')
emitWarningLog('Downloaded ' + percent + '% of blockchain from network');
});
};
generateProgress(currentBlocks);
setInterval(function(){
_this.daemon.cmd('getblocktemplate', [], function(results){
var synced = results.every(function(r){
return r.error.code !== -10;
});
if (synced){
SetupDaemonInterface();
}
else{
_this.daemon.cmd('getinfo', [], function(results){
var smallestHeight = results.sort(function(a, b){
return b.response.blocks - a.response.blocks;
})[0].response.blocks;
generateProgress(smallestHeight);
});
}
});
}, 5000);
}
function StartStratumServer(){
_this.stratumServer = new stratum.Server(options.ports, options.connectionTimeout, options.banning, authorizeFn);
_this.stratumServer.on('started', function(){
emitLog('Stratum server started on port(s): ' + Object.keys(options.ports).join(', '));
_this.emit('started');
}).on('client.connected', function(client){
if (typeof(_this.varDiff[client.socket.localPort]) !== 'undefined') {
_this.varDiff[client.socket.localPort].manageClient(client);
}
client.on('difficultyChanged', function(diff){
_this.emit('difficultyUpdate', client.workerName, diff);
}).on('subscription', function(params, resultCallback){
var extraNonce = _this.jobManager.extraNonceCounter.next();
@ -370,14 +436,19 @@ var pool = module.exports = function pool(options, authorizeFn){
}).on('malformedMessage', function (message) {
emitWarningLog(client.workerName + " has sent us a malformed message: " + message);
}).on('socketError', function(err) {
emitWarningLog(client.workerName + " has somehow had a socket error: " + JSON.stringify(err));
}).on('socketDisconnect', function() {
emitLog("Client '" + client.workerName + "' disconnected!");
}).on('unknownStratumMethod', function(fullMessage) {
emitLog("Client '" + client.workerName + "' has sent us an unknown stratum method: " + fullMessage.method);
}).on('socketFlooded', function(){
emitWarningLog('Detected socket flooding and purged buffer');
}).on('ban', function(ipAddress){
_this.emit('banIP', ipAddress);
emitWarningLog('banned IP ' + ipAddress);
@ -400,16 +471,12 @@ var pool = module.exports = function pool(options, authorizeFn){
emitLog('Block polling every ' + pollingInterval + ' milliseconds');
}
function GetBlockTemplate(daemonObj, callback){
if (typeof(callback) === 'undefined') {
callback = daemonObj;
daemonObj = _this.daemon;
}
daemonObj.cmd('getblocktemplate',
function GetBlockTemplate(callback){
_this.daemon.cmd('getblocktemplate',
[{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}],
function(result){
if (result.error){
emitErrorLog('system', 'getblocktemplate call failed for daemon instance ' +
emitErrorLog('getblocktemplate call failed for daemon instance ' +
result.instance.index + ' with error ' + JSON.stringify(result.error));
callback(result.error);
} else {
@ -458,7 +525,7 @@ var pool = module.exports = function pool(options, authorizeFn){
if (typeof(_this.jobManager.currentJob) !== 'undefined' && blockHash !== _this.jobManager.currentJob.rpcData.previousblockhash){
GetBlockTemplate(function(error, result){
if (error)
emitErrorLog('system', 'Block notify error getting block template for ' + options.coin.name);
emitErrorLog('Block notify error getting block template for ' + options.coin.name);
})
}
};
@ -508,9 +575,6 @@ var pool = module.exports = function pool(options, authorizeFn){
};
this.setVarDiff = function(port, varDiffInstance) {
if (typeof(_this.varDiff) === 'undefined') {
_this.varDiff = {};
}
if (typeof(_this.varDiff[port]) != 'undefined' ) {
_this.varDiff[port].removeAllListeners();
}