improvements
This commit is contained in:
parent
b8d8f88296
commit
7e5241a6e2
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -73,7 +73,7 @@ function DaemonInterface(options){
|
||||
});
|
||||
res.on('end', function(){
|
||||
var dataJson = JSON.parse(data);
|
||||
callback(null, dataJson);
|
||||
callback(null, dataJson.result);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
1
main.js
1
main.js
@ -12,6 +12,7 @@ var coins = [
|
||||
name: 'Dogecoin',
|
||||
symbol: 'doge',
|
||||
algorithm: 'scrypt',
|
||||
reward: 'POW', //or POS
|
||||
address: 'D5uXR7F6bTCJKRZBqj1D4gyHF9MHAd5oNs',
|
||||
daemon: {
|
||||
bin: 'dogecoind',
|
||||
|
||||
55
pool.js
55
pool.js
@ -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);
|
||||
});
|
||||
|
||||
14
stratum.js
14
stratum.js
@ -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)
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
184
transactions.js
184
transactions.js
@ -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
33
util.js
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user