improvements

This commit is contained in:
Matthew Little 2014-01-06 23:15:27 -05:00
parent b8d8f88296
commit 7e5241a6e2
9 changed files with 247 additions and 66 deletions

View File

@ -11,14 +11,14 @@ Features (mostly untested)
* Daemon interface
* Stratum TCP socket server
* Block template / job manager
* Optimized generation transaction building
#### To do
* Optimized generation transaction building
* Integrate with PostgreSQL database
* Handle share submissions
* Payment processing module
* Support more algos (scrypt, scrypt-jane, quark)
* Statistics module
* Integrate with PostgreSQL database
* Web frontend
@ -32,6 +32,7 @@ Requirements
* [bignum](https://github.com/justmoon/node-bignum)
* [buffertools] (https://github.com/bnoordhuis/node-buffertools)
* [base58-native](https://github.com/gasteve/node-base58)
* [async](https://github.com/caolan/async)
Credits

View File

@ -6,7 +6,7 @@ var transactions = require('./transactions.js');
var util = require('./util.js');
var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, address){
var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publicKey, reward){
//private members
@ -30,11 +30,10 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, addr
this.jobId = jobId;
this.merkleTree = new merkleTree(getTransactionBuffers(rpcData.transactions));
this.merkleBranch = getMerkleHashes(this.merkleTree.steps);
this.coinbase = new transactions.Generation(
rpcData.coinbasevalue,
rpcData.coinbaseaux.flags,
rpcData.height,
address
this.generationTransaction = new transactions.Generation(
rpcData,
publicKey,
reward
);
this.getJobParams = function(){
@ -42,8 +41,8 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, addr
this.jobParams = [
this.jobId,
util.reverseHex(this.rpcData.previousblockhash),
this.coinbase.serialized[0].toString('hex'),
this.coinbase.serialized[1].toString('hex'),
this.generationTransaction.coinbase[0].toString('hex'),
this.generationTransaction.coinbase[1].toString('hex'),
this.merkleBranch,
binpack.packInt32(this.rpcData.version, 'big').toString('hex'),
this.rpcData.bits,

View File

@ -73,7 +73,7 @@ function DaemonInterface(options){
});
res.on('end', function(){
var dataJson = JSON.parse(data);
callback(null, dataJson);
callback(null, dataJson.result);
});
});

View File

@ -67,8 +67,8 @@ var JobManager = module.exports = function JobManager(options){
this.extraNonceCounter = new ExtraNonceCounter();
this.currentJob;
this.newTemplate = function(rpcData){
this.currentJob = new blockTemplate(jobCounter.next(), rpcData, options.address);
this.newTemplate = function(rpcData, publicKey){
this.currentJob = new blockTemplate(jobCounter.next(), rpcData, publicKey);
jobs[this.currentJob.jobId] = this.currentJob;
CheckNewIfNewBlock(this.currentJob);
};

View File

@ -12,6 +12,7 @@ var coins = [
name: 'Dogecoin',
symbol: 'doge',
algorithm: 'scrypt',
reward: 'POW', //or POS
address: 'D5uXR7F6bTCJKRZBqj1D4gyHF9MHAd5oNs',
daemon: {
bin: 'dogecoind',

55
pool.js
View File

@ -2,6 +2,7 @@ var net = require('net');
var events = require('events');
var bignum = require('bignum');
var async = require('async');
var daemon = require('./daemon.js');
var stratum = require('./stratum.js');
@ -14,6 +15,7 @@ var transactions = require('./transactions.js');
var pool = module.exports = function pool(coin){
var _this = this;
var publicKeyBuffer;
this.jobManager = new jobManager({
algorithm: coin.options.algorithm,
@ -26,15 +28,52 @@ var pool = module.exports = function pool(coin){
this.daemon = new daemon.interface(coin.options.daemon);
this.daemon.on('online', function(){
this.cmd(
'getblocktemplate',
[{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}],
function(error, response){
_this.jobManager.newTemplate(response.result);
console.log(response.result);
//console.log(_this.jobManager.currentJob.getJobParams());
async.parallel({
rpcTemplate: function(callback){
_this.daemon.cmd('getblocktemplate',
[{"capabilities": [ "coinbasetxn", "workid", "coinbase/append" ]}],
function(error, result){
if (error){
console.log('getblocktemplate rpc error for ' + coin.options.name);
callback(error);
}
else
callback(null, result);
}
);
},
addressInfo: function(callback){
_this.daemon.cmd('validateaddress',
[coin.options.address],
function(error, result){
if (error){
console.log('validateaddress rpc error for ' + coin.options.name);
callback(error);
}
else if (!result.isvalid){
console.log('address is not valid for ' + coin.options.name);
callback(error);
}
else
callback(error, result);
}
);
}
);
}, function(err, results){
if (err) return;
//console.log(results);
publicKeyBuffer = coin.options.reward === 'POW' ?
util.script_to_address(results.addressInfo.address) :
util.script_to_pubkey(results.addressInfo.pubkey);
_this.jobManager.newTemplate(results.rpcTemplate, publicKeyBuffer);
console.log(_this.jobManager.currentJob.getJobParams());
});
}).on('startFailed', function(){
console.log('Failed to start daemon for ' + coin.name);
});

View File

@ -8,7 +8,7 @@ var util = require('./util.js');
var SubscriptionCounter = function(){
var count = 0;
var padding = 'deadbeefdeadbeef';
var padding = 'deadbeefcafebabe';
return {
next: function(){
count++;
@ -167,15 +167,15 @@ var StratumServer = exports.Server = function StratumServer(options){
//private members
var _this = this;
var _socketServer;
var _stratumClients = {};
var _subscriptionCounter = SubscriptionCounter();
var socketServer;
var stratumClients = {};
var subscriptionCounter = SubscriptionCounter();
(function init(){
_socketServer = socketServer = net.createServer(function(c){
var subscriptionId = _subscriptionCounter.next();
var subscriptionId = subscriptionCounter.next();
var client = new StratumClient({subscriptionId: subscriptionId, socket: c});
_stratumClients[subscriptionId] = client;
stratumClients[subscriptionId] = client;
_this.emit('client', client);
});
_socketServer.listen(options.port, function(){});
@ -186,7 +186,7 @@ var StratumServer = exports.Server = function StratumServer(options){
this.broadcastMiningJobs = function(jobParams){
for (var clientId in _stratumClients){
_stratumClients[clientId].sendMiningJob(jobParams)
stratumClients[clientId].sendMiningJob(jobParams)
}
};
};

View File

@ -1,16 +1,153 @@
/*
Ported from https://github.com/slush0/stratum-mining
*/
var binpack = require('binpack');
var buffertools = require('buffertools');
var util = require('./util.js');
var extranonce_placeholder = new Buffer('f000000ff111111f', 'hex');
exports.extranonce_size = extranonce_placeholder.length;
function Transaction(params){
var version;
var inputs;
var outputs;
var lockTime;
(function init(){
if (typeof(params) === "object"){
version = params.version || 1;
inputs = params.inputs || [];
outputs = params.outputs || [];
lockTime = params.lockTime || 0;
}
else if (typeof(params) === "string"){
fromRaw(params);
}
})();
function fromRaw(raw){
}
this.toBuffer = function(){
return Buffer.concat([
binpack.packUInt32(version, 'little'),
util.varIntBuffer(inputs.length),
Buffer.concat(inputs.map(function(i){ return i.toBuffer() })),
util.varIntBuffer(outputs.length),
Buffer.concat(outputs.map(function(o){ return o.toBuffer() })),
binpack.packUInt32(lockTime, 'little')
]);
};
this.inputs = inputs;
this.outputs = outputs;
}
function TransactionInput(params){
var prevOutHash;
var prevOutIndex;
var sigScriptBuffer;
var sequence;
(function init(){
if (typeof(params) === "object"){
prevOutHash = params.prevOutHash || 0;
prevOutIndex = params.prevOutIndex;
sigScriptBuffer = params.sigScriptBuffer;
sequence = params.sequence || 0;
}
else if (typeof(params) === "string"){
fromRaw(params);
}
})();
function fromRaw(raw){
}
this.toBuffer = function(){
return Buffer.concat([
util.uint256BufferFromHash(prevOutHash),
binpack.packUInt32(prevOutIndex, 'little'),
util.varIntBuffer(sigScriptBuffer.length),
sigScriptBuffer,
binpack.packUInt32(sequence)
]);
};
}
function TransactionOutput(params){
var value;
var pkScriptBuffer;
(function init(){
if (typeof(params) === "object"){
value = params.value;
pkScriptBuffer = params.pkScriptBuffer;
}
else if (typeof(params) === "string"){
fromRaw(params);
}
})();
function fromRaw(raw){
}
this.toBuffer = function(){
return Buffer.concat([
binpack.packInt64(value, 'little'),
util.varIntBuffer(pkScriptBuffer.length),
pkScriptBuffer
]);
};
}
var buildScriptSig = function(height, flags){
return Buffer.concat([
util.serializeNumber(height),
new Buffer(flags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([exports.extranonce_size]),
extranonce_placeholder,
util.ser_string('/nodeStratum/')
]);
};
var Generation = exports.Generation = function Generation(rpcData, publicKey){
var scriptSig = buildScriptSig(rpcData.height, rpcData.coinbaseaux.flags);
var tx = new Transaction({
inputs: [new TransactionInput({
prevOutIndex: Math.pow(2, 32) - 1,
sigScriptBuffer: scriptSig
})],
outputs: [new TransactionOutput({
value: rpcData.coinbasevalue,
pkScriptBuffer: publicKey
})]
});
var txBuffer = tx.toBuffer();
var epIndex = buffertools.indexOf(txBuffer, extranonce_placeholder);
var p1 = txBuffer.slice(0, epIndex);
var p2 = txBuffer.slice(epIndex + extranonce_placeholder.length);
this.transaction = tx;
this.coinbase = [p1, p2];
};
/*
function COutPoint(){
this.hash = 0;
this.n = 0;
@ -97,36 +234,6 @@ CTransaction.prototype = {
exports.CTransaction = CTransaction;
var extranonce_placeholder = new Buffer('f000000ff111111f', 'hex');
exports.extranonce_size = extranonce_placeholder.length;
var GenerationNew = function(blockTemplate, address){
return Buffer.concat([
binpack.packInt32(1, 'little'), //transaction version
new Buffer([1]), //length of transaction inputs (which is 1, the coinbase)
Buffer.concat([ //serialized coinbase tx input
Buffer.concat([ //prevout
util.uint256BufferFromHash(0), //hash
binpack.packUInt32(Math.pow(2, 32) - 1, 'little') //index
]),
util.ser_string(Buffer.concat([ //script length (varint), script
Buffer.concat([
util.serializeNumber(blockTemplate.rpcData.height),
new Buffer(blockTemplate.rpcData.coinbaseaux.flags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([exports.extranonce_size])
]),
extranonce_placeholder,
util.ser_string('/stratum/')
])),
binpack.packUInt32(0, 'little') //sequence number
]),
util.ser_vector(this.vout),
binpack.packUInt32(0, 'little') //locktime
]);
};
var Generation = exports.Generation = function Generation(coinbaseValue, coinbaseAuxFlags, height, address){
var CTrans = new CTransaction();
@ -179,4 +286,5 @@ Generation.prototype = {
]);
}
};
};
*/

33
util.js
View File

@ -194,6 +194,29 @@ exports.deser_string = function(f){
return f.read(nit);
};
exports.varIntBuffer = function(n){
if (n < 0xfd)
return new Buffer([n]);
else if (n < 0xffff){
var buff = new Buffer(3);
buff[0] = 0xfd;
buff.writeUInt16LE(n, 1);
return buff;
}
else if (n < 0xffffffff){
var buff = new Buffer(5);
buff[0] = 0xfe;
buff.writeUInt32LE(n, 1);
return buff;
}
else{
var buff = new Buffer(9);
buff[0] = 0xff;
binpack.packUInt64(n, 'little').copy(buff, 1);
return buff;
}
};
exports.ser_vector = function(l){
var r;
if (l.length < 253)
@ -268,6 +291,16 @@ exports.address_to_pubkeyhash = function(addr){
return [ver, addr.slice(1,-4)];
};
exports.script_to_pubkey = function(key){
if (key.length === 66) key = new Buffer(key, 'hex');
if (key !== 33) throw 'Invalid address';
var pubkey = new Buffer(35);
pubkey[0] = 0x21;
pubkey[34] = 0xac;
key.copy(pubkey, 1);
return pubkey;
};
exports.script_to_address = function(addr){
var d = exports.address_to_pubkeyhash(addr)
if (!d)