Merge branch 'vekexasia'
Conflicts: example/coins/dogecoin.json example/init.js index.js
This commit is contained in:
commit
be51126f09
11
TODOLIST.md
Normal file
11
TODOLIST.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
#### TODO
|
||||||
|
* vekexasia: handle socket error from client so that we could clean up stratumServers Client
|
||||||
|
* vekexasia: fix check of difficulty of the shares. Not sure why a lot of shares are marked as "low-submit-share"
|
||||||
|
|
||||||
|
#### TOFIX
|
||||||
|
* vekexasia: jobManager.js has implement the expected block hash after submitting it to the daemon. This will let us check if the block was accepted by the daemon.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### DONE
|
||||||
@ -1,30 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
var net = require('net');
|
|
||||||
|
|
||||||
var config = process.argv[1];
|
|
||||||
var parts = config.split(':');
|
|
||||||
var host = parts[0];
|
|
||||||
var port = parts[1];
|
|
||||||
var password = process.argv[2];
|
|
||||||
var coin = process.argv[3];
|
|
||||||
var blockHash = process.argv[4];
|
|
||||||
|
|
||||||
var client = net.connect(port, host, function() {
|
|
||||||
console.log('client connected');
|
|
||||||
client.write(JSON.stringify({
|
|
||||||
password: password,
|
|
||||||
coin: coin,
|
|
||||||
blockHash: blockHash
|
|
||||||
}) + '\n');
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('data', function(data) {
|
|
||||||
console.log(data.toString());
|
|
||||||
//client.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('end', function() {
|
|
||||||
console.log('client disconnected');
|
|
||||||
//process.exit();
|
|
||||||
});
|
|
||||||
@ -10,8 +10,9 @@
|
|||||||
"merkleRefreshInterval": 60,
|
"merkleRefreshInterval": 60,
|
||||||
"daemon": {
|
"daemon": {
|
||||||
"host": "localhost",
|
"host": "localhost",
|
||||||
"port": 8332,
|
"port": 19334,
|
||||||
"user": "test",
|
"user": "testnet",
|
||||||
"password": "test"
|
"password": "testnet1"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +1,7 @@
|
|||||||
var net = require('net');
|
var net = require('net');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var pool = require('../index.js');
|
||||||
var pool = require('./pool.js');
|
|
||||||
var ShareManager = require('./shareManager.js').ShareManager;
|
var ShareManager = require('./shareManager.js').ShareManager;
|
||||||
|
|
||||||
var logRef = console.log;
|
var logRef = console.log;
|
||||||
@ -24,6 +23,18 @@ var coins = [];
|
|||||||
|
|
||||||
var confFolder = 'coins';
|
var confFolder = 'coins';
|
||||||
|
|
||||||
|
|
||||||
|
var authorizeFN = function (ip, workerName, password, cback) {
|
||||||
|
// Default implementation just returns true
|
||||||
|
console.log("Athorize ["+ip+"] "+workerName+":"+password);
|
||||||
|
cback(
|
||||||
|
null, // error
|
||||||
|
true, // authorized?
|
||||||
|
false, // should disconnect user?
|
||||||
|
16 // difficulty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fs.readdir(confFolder, function(err, files){
|
fs.readdir(confFolder, function(err, files){
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
@ -35,8 +46,8 @@ fs.readdir(confFolder, function(err, files){
|
|||||||
var coin = new Coin(coinJson);
|
var coin = new Coin(coinJson);
|
||||||
console.log('Starting pool for ' + coin.options.name);
|
console.log('Starting pool for ' + coin.options.name);
|
||||||
|
|
||||||
coin.pool = new pool(coin);
|
coin.pool = new pool(coin, authorizeFN );
|
||||||
coin.shareManager = new ShareManager(coin.pool);
|
var shareManager = new ShareManager(coin.pool);
|
||||||
|
|
||||||
coins.push(coin);
|
coins.push(coin);
|
||||||
});
|
});
|
||||||
@ -11,28 +11,27 @@ var ShareManager = exports.ShareManager = function(pool) {
|
|||||||
pool.on('share', function(isValid, data) {
|
pool.on('share', function(isValid, data) {
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
handleValidShare(
|
handleValidShare(
|
||||||
data.workerName,
|
data.client,
|
||||||
data.blockHeaderHex,
|
data.blockHeaderHex,
|
||||||
data.jobId,
|
data.jobId,
|
||||||
data.clientDifficulty,
|
|
||||||
data.extraNonce1,
|
data.extraNonce1,
|
||||||
data.extraNonce2,
|
data.extraNonce2,
|
||||||
data.nTime,
|
data.nTime,
|
||||||
data.nonce);
|
data.nonce);
|
||||||
} else {
|
} else {
|
||||||
handleInvalidShare(
|
handleInvalidShare(
|
||||||
data.workerName,
|
data.client,
|
||||||
data.error[0],
|
data.error[0],
|
||||||
data.error[1]);
|
data.error[1]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleValidShare(workerName, headerHex, jobId, clientDifficulty, extraNonce1, extraNonce2, nTime, nonce) {
|
function handleValidShare(client, headerHex, jobId, extraNonce1, extraNonce2, nTime, nonce) {
|
||||||
console.log("A new Valid share from "+workerName+" has arrived! - "+headerHex);
|
console.log("A new Valid share from "+client.workerName+" has arrived! - "+headerHex);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleInvalidShare(workerName, errorCode, errorDescription) {
|
function handleInvalidShare(client, errorCode, errorDescription) {
|
||||||
console.log("Invalid share form "+workerName+" ErrorCode: "+errorCode+ " ErrorDescription: "+errorDescription);
|
console.log("Invalid share form "+client.workerName+" ErrorCode: "+errorCode+ " ErrorDescription: "+errorDescription);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1,15 +1,18 @@
|
|||||||
var net = require('net');
|
var net = require('net');
|
||||||
var events = require('events');
|
var events = require('events');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
var async = require('async');
|
||||||
|
var daemon = require('./libs/daemon.js');
|
||||||
|
var stratum = require('./libs/stratum.js');
|
||||||
|
var jobManager = require('./libs/jobManager.js');
|
||||||
|
var util = require('./libs/util.js');
|
||||||
|
|
||||||
var async = require('async');
|
/**
|
||||||
|
* Main pool object. It emits the following events:
|
||||||
var daemon = require('./daemon.js');
|
* - 'started'() - when the pool is effectively started.
|
||||||
var stratum = require('./stratum.js');
|
* - 'share'(isValid, dataObj) - In case it's valid the dataObj variable will contain (TODO) and in case it's invalid (TODO)
|
||||||
var jobManager = require('./jobManager.js');
|
*/
|
||||||
var util = require('./util.js');
|
var pool = module.exports = function pool(coin, authFn){
|
||||||
|
|
||||||
var pool = module.exports = function pool(coin){
|
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var publicKeyBuffer;
|
var publicKeyBuffer;
|
||||||
@ -114,26 +117,26 @@ var pool = module.exports = function pool(coin){
|
|||||||
function StartStratumServer(){
|
function StartStratumServer(){
|
||||||
console.log('Stratum server starting on port ' + coin.options.stratumPort + ' for ' + coin.options.name);
|
console.log('Stratum server starting on port ' + coin.options.stratumPort + ' for ' + coin.options.name);
|
||||||
_this.stratumServer = new stratum.Server({
|
_this.stratumServer = new stratum.Server({
|
||||||
port: coin.options.stratumPort
|
port : coin.options.stratumPort,
|
||||||
|
authorizeFn : authorizeFn,
|
||||||
});
|
});
|
||||||
_this.stratumServer.on('started', function(){
|
_this.stratumServer.on('started', function(){
|
||||||
console.log('Stratum server started on port ' + coin.options.stratumPort + ' for ' + coin.options.name);
|
console.log('Stratum server started on port ' + coin.options.stratumPort + ' for ' + coin.options.name);
|
||||||
}).on('client', function(client){
|
}).on('client.connected', function(client){
|
||||||
client.on('subscription', function(params, resultCallback){
|
client.on('subscription', function(params, resultCallback){
|
||||||
|
|
||||||
var extraNonce = _this.jobManager.extraNonceCounter.next();
|
var extraNonce = _this.jobManager.extraNonceCounter.next();
|
||||||
var extraNonce2Size = _this.jobManager.extraNonce2Size;
|
var extraNonce2Size = _this.jobManager.extraNonce2Size;
|
||||||
resultCallback(null,
|
resultCallback(null,
|
||||||
extraNonce,
|
extraNonce,
|
||||||
extraNonce2Size
|
extraNonce2Size
|
||||||
);
|
);
|
||||||
this.sendDifficulty(coin.options.difficulty);
|
var clientThis = this;
|
||||||
if (typeof(_this.jobManager.currentJob) === 'undefined') {
|
|
||||||
console.warn("[subscription] Cannot send job to client. No jobs in jobManager!");
|
//if (clientThis.authorized) {
|
||||||
} else {
|
clientThis.sendMiningJob(_this.jobManager.currentJob.getJobParams());
|
||||||
this.sendMiningJob(_this.jobManager.currentJob.getJobParams());
|
//}
|
||||||
}
|
|
||||||
}).on('authorize', function(params, resultCallback){
|
|
||||||
resultCallback(null, true);
|
|
||||||
}).on('submit', function(params, resultCallback){
|
}).on('submit', function(params, resultCallback){
|
||||||
var result =_this.jobManager.processShare(
|
var result =_this.jobManager.processShare(
|
||||||
params.jobId,
|
params.jobId,
|
||||||
@ -146,20 +149,19 @@ var pool = module.exports = function pool(coin){
|
|||||||
if (result.error){
|
if (result.error){
|
||||||
resultCallback(result.error);
|
resultCallback(result.error);
|
||||||
_this.emit('share', false, {
|
_this.emit('share', false, {
|
||||||
workerName : params.name,
|
client : client,
|
||||||
error : result.error
|
error : result.error
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
resultCallback(null, true);
|
resultCallback(null, true);
|
||||||
_this.emit('share', true, {
|
_this.emit('share', true, {
|
||||||
blockHeaderHex : result.headerHEX,
|
client : client,
|
||||||
workerName : params.name,
|
blockHeaderHex : result.headerHEX,
|
||||||
jobId : params.jobId,
|
workerName : params.name,
|
||||||
clientDifficulty : client.difficulty,
|
jobId : params.jobId,
|
||||||
extraNonce1 : client.extraNonce1,
|
extraNonce2 : params.extraNonce2,
|
||||||
extraNonce2 : params.extraNonce2,
|
nTime : params.nTime,
|
||||||
nTime : params.nTime,
|
nonce : params.nonce
|
||||||
nonce : params.nonce
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,11 +1,13 @@
|
|||||||
|
var binpack = require('binpack');
|
||||||
var binpack = require('binpack');
|
var merkleTree = require('./merkleTree.js');
|
||||||
|
|
||||||
var merkleTree = require('./merkleTree.js');
|
|
||||||
var transactions = require('./transactions.js');
|
var transactions = require('./transactions.js');
|
||||||
var util = require('./util.js');
|
var util = require('./util.js');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BlockTemplate class holds a single job.
|
||||||
|
* and provides serveral methods to validate and submit it to the daemon coin
|
||||||
|
**/
|
||||||
var BlockTemplate = module.exports = function BlockTemplate(rpcData, publicKey, extraNoncePlaceholder){
|
var BlockTemplate = module.exports = function BlockTemplate(rpcData, publicKey, extraNoncePlaceholder){
|
||||||
|
|
||||||
//private members
|
//private members
|
||||||
@ -1,10 +1,16 @@
|
|||||||
var http = require('http');
|
var http = require('http');
|
||||||
var cp = require('child_process');
|
var cp = require('child_process');
|
||||||
var events = require('events');
|
var events = require('events');
|
||||||
|
|
||||||
|
|
||||||
var startFailedTimeout = 120; //seconds
|
var startFailedTimeout = 120; //seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The daemon interface interacts with the coin daemon by using the rpc interface.
|
||||||
|
* in otder to make it work it needs, as constructor, an object containing
|
||||||
|
* - 'host' : hostname where the coin lives
|
||||||
|
* - 'port' : port where the coin accepts rpc connections
|
||||||
|
* - 'user' : username of the coin for the rpc interface
|
||||||
|
* - 'password': password for the rpc interface of the coin
|
||||||
|
**/
|
||||||
|
|
||||||
function DaemonInterface(options){
|
function DaemonInterface(options){
|
||||||
|
|
||||||
@ -61,11 +67,11 @@ function DaemonInterface(options){
|
|||||||
}
|
}
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
hostname: 'localhost',
|
hostname: (typeof(_this.options.host) === 'undefined'?'localhost':_this.options.host),
|
||||||
port: _this.options.port,
|
port : _this.options.port,
|
||||||
method: 'POST',
|
method : 'POST',
|
||||||
auth: _this.options.user + ':' + _this.options.password,
|
auth : _this.options.user + ':' + _this.options.password,
|
||||||
headers: {
|
headers : {
|
||||||
'Content-Length': requestJson.length
|
'Content-Length': requestJson.length
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -44,7 +44,11 @@ var JobCounter = function(){
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emits:
|
||||||
|
* - 'newBlock'(blockTemplate) - when a new block (previously unknown to the JobManager) is being added
|
||||||
|
* - 'blockFound'(serializedBlock) - when a worker finds a block.
|
||||||
|
**/
|
||||||
var JobManager = module.exports = function JobManager(options){
|
var JobManager = module.exports = function JobManager(options){
|
||||||
|
|
||||||
//private members
|
//private members
|
||||||
@ -102,6 +106,25 @@ var JobManager = module.exports = function JobManager(options){
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to estimate the resulting block hash
|
||||||
|
* This is only valid for scrypt apparently.
|
||||||
|
* @author vekexasia
|
||||||
|
**/
|
||||||
|
function blockHashHex(headerBuffer) {
|
||||||
|
var result = new Buffer(80);
|
||||||
|
for (var i=0; i<20; i++) {
|
||||||
|
for (var j=0; j<4; j++) {
|
||||||
|
result[i*4+j] = headerBuffer[i*4+3-j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var shaed = util.reverseBuffer(util.doublesha(result));
|
||||||
|
|
||||||
|
|
||||||
|
return shaed.toString('hex'); // return the expected block hash
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//public members
|
//public members
|
||||||
|
|
||||||
this.extraNonceCounter = new ExtraNonceCounter();
|
this.extraNonceCounter = new ExtraNonceCounter();
|
||||||
@ -128,45 +151,55 @@ var JobManager = module.exports = function JobManager(options){
|
|||||||
return {error: [20, 'incorrect size of extranonce2', null]};
|
return {error: [20, 'incorrect size of extranonce2', null]};
|
||||||
|
|
||||||
var job = jobs[jobId];
|
var job = jobs[jobId];
|
||||||
if (!job)
|
if (!job) {
|
||||||
return {error: [21, 'job not found', null]};
|
return {error: [21, 'job not found', null]};
|
||||||
|
}
|
||||||
|
|
||||||
if (nTime.length !== 8)
|
if (nTime.length !== 8) {
|
||||||
return {error: [20, 'incorrect size of ntime']};
|
return {error: [20, 'incorrect size of ntime']};
|
||||||
|
}
|
||||||
|
|
||||||
var nTimeInt = parseInt(nTime, 16);
|
var nTimeInt = parseInt(nTime, 16);
|
||||||
if (nTimeInt < job.rpcData.curtime || nTime > submitTime + 7200)
|
if (nTimeInt < job.rpcData.curtime || nTime > submitTime + 7200) {
|
||||||
return {error: [20, 'ntime out of range', null]};
|
return {error: [20, 'ntime out of range', null]};
|
||||||
|
}
|
||||||
|
|
||||||
if (nonce.length !== 8)
|
if (nonce.length !== 8) {
|
||||||
return {error: [20, 'incorrect size of nonce']};
|
return {error: [20, 'incorrect size of nonce']};
|
||||||
|
}
|
||||||
|
|
||||||
if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce))
|
if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce)) {
|
||||||
return {error: [22, 'duplicate share', null]};
|
return {error: [22, 'duplicate share', null]};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var extraNonce1Buffer = new Buffer(extraNonce1, 'hex');
|
var extraNonce1Buffer = new Buffer(extraNonce1, 'hex');
|
||||||
var extraNonce2Buffer = new Buffer(extraNonce2, 'hex');
|
var extraNonce2Buffer = new Buffer(extraNonce2, 'hex');
|
||||||
|
|
||||||
var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer);
|
var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer);
|
||||||
var coinbaseHash = util.doublesha(coinbaseBuffer);
|
var coinbaseHash = util.doublesha(coinbaseBuffer);
|
||||||
|
|
||||||
var merkleRoot = job.merkleTree.withFirst(coinbaseHash);
|
var merkleRoot = job.merkleTree.withFirst(coinbaseHash);
|
||||||
merkleRoot = util.reverseBuffer(merkleRoot).toString('hex');
|
merkleRoot = util.reverseBuffer(merkleRoot).toString('hex');
|
||||||
|
|
||||||
var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce);
|
var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce);
|
||||||
var headerHash = hashDigest(headerBuffer, nTimeInt);
|
var headerHash = hashDigest(headerBuffer, nTimeInt);
|
||||||
var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32});
|
var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32});
|
||||||
|
|
||||||
var targetUser = bignum(diffDividend / difficulty);
|
|
||||||
if (headerBigNum.gt(targetUser)){
|
|
||||||
return {error: [23, 'low difficulty share', null]};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (job.target.ge(headerBigNum)){
|
if (job.target.ge(headerBigNum)){
|
||||||
var blockBuf = job.serializeBlock(headerBuffer, coinbaseBuffer);
|
var blockBuf = job.serializeBlock(headerBuffer, coinbaseBuffer);
|
||||||
|
console.log("EXPECTED BLOCK HASH: "+blockHashHex(headerBuffer)); // NOT WORKING :(?
|
||||||
|
_this.emit('blockFound', blockBuf.toString('hex'));
|
||||||
|
} else {
|
||||||
|
// If block is not found we want also to check the difficulty of the share.
|
||||||
|
// TODO: this seems to not be working properly
|
||||||
|
var targetUser = bignum(diffDividend / difficulty);
|
||||||
|
|
||||||
_this.emit('blockFound', blockBuf.toString('hex'), headerBigNum.toString(16), coinbaseHash.toString('hex'));
|
if (headerBigNum.gt(targetUser)){
|
||||||
|
return {error: [23, 'low difficulty share', null]};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {result: true, headerHEX: headerBigNum.toString(16)};
|
return {result: true, headerHEX: headerBigNum.toString(16)};
|
||||||
301
libs/stratum.js
Normal file
301
libs/stratum.js
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
var net = require('net');
|
||||||
|
var events = require('events');
|
||||||
|
|
||||||
|
var binpack = require('binpack');
|
||||||
|
|
||||||
|
var util = require('./util.js');
|
||||||
|
|
||||||
|
|
||||||
|
var SubscriptionCounter = function(){
|
||||||
|
var count = 0;
|
||||||
|
var padding = 'deadbeefcafebabe';
|
||||||
|
return {
|
||||||
|
next: function(){
|
||||||
|
count++;
|
||||||
|
if (Number.MAX_VALUE === count) count = 0;
|
||||||
|
return padding + binpack.packUInt64(count, 'big').toString('hex');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defining each client that connects to the stratum server.
|
||||||
|
* Emits:
|
||||||
|
* - 'subscription'(obj, cback(error, extraNonce1, extraNonce2Size))
|
||||||
|
* - 'submit' FIX THIS.
|
||||||
|
**/
|
||||||
|
var StratumClient = function(options){
|
||||||
|
|
||||||
|
//private members
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
(function init(){
|
||||||
|
setupSocket();
|
||||||
|
})();
|
||||||
|
|
||||||
|
function handleMessage(message){
|
||||||
|
switch(message.method){
|
||||||
|
case 'mining.subscribe':
|
||||||
|
handleSubscribe(message);
|
||||||
|
break;
|
||||||
|
case 'mining.authorize':
|
||||||
|
handleAuthorize(message);
|
||||||
|
break;
|
||||||
|
case 'mining.submit':
|
||||||
|
handleSubmit(message);
|
||||||
|
break;
|
||||||
|
case 'mining.get_transactions':
|
||||||
|
sendJson({
|
||||||
|
id : null,
|
||||||
|
result : [],
|
||||||
|
error : true
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.dir('unknown stratum client message: ' + JSON.stringify(message));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubscribe(message){
|
||||||
|
if (! _this._authorized ) {
|
||||||
|
_this.requestedSubscriptionBeforeAuth = true;
|
||||||
|
}
|
||||||
|
_this.emit('subscription',
|
||||||
|
{},
|
||||||
|
function(error, extraNonce1, extraNonce2Size){
|
||||||
|
if (error){
|
||||||
|
sendJson({
|
||||||
|
id: message.id,
|
||||||
|
result: null,
|
||||||
|
error: error
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_this.extraNonce1 = extraNonce1;
|
||||||
|
sendJson({
|
||||||
|
id: message.id,
|
||||||
|
result: [
|
||||||
|
["mining.notify", options.subscriptionId],
|
||||||
|
extraNonce1,
|
||||||
|
extraNonce2Size
|
||||||
|
],
|
||||||
|
error: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAuthorize(message){
|
||||||
|
_this.workerIP = options.socket.address().address;
|
||||||
|
_this.workerName = message.params[0];
|
||||||
|
_this.workerPass = message.params[1];
|
||||||
|
options.authorizeFn(_this.workerIP, _this.workerName, _this.workerPass, function(err, authorized, shouldCloseSocket, difficulty) {
|
||||||
|
_this.authorized = ( ! err && authorized );
|
||||||
|
sendJson({
|
||||||
|
id : message.id,
|
||||||
|
result : _this.authorized,
|
||||||
|
error : err
|
||||||
|
});
|
||||||
|
|
||||||
|
// If the authorizer wants us to close the socket lets do it.
|
||||||
|
if (typeof(shouldCloseSocket) === 'boolean' && shouldCloseSocket) {
|
||||||
|
options.socket.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send difficulty
|
||||||
|
if (typeof(difficulty) === 'function') {
|
||||||
|
difficulty(_this.workerName, function(err, diff) {
|
||||||
|
if (err) {
|
||||||
|
console.error("Cannot set difficulty for "+_this.workernName+" error: "+JSON.stringify(err));
|
||||||
|
options.socket.end();
|
||||||
|
} else {
|
||||||
|
_this.sendAndSetDifficultyIfNew(diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
} else if (typeof(difficulty) === 'number') {
|
||||||
|
_this.sendAndSetDifficultyIfNew(difficulty);
|
||||||
|
} else {
|
||||||
|
process.exit("Difficulty from authorizeFn callback is neither a function or a number");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_this.requestedSubscriptionBeforeAuth === true) {
|
||||||
|
delete _this.requestedSubscriptionBeforeAuth; // we do not need this anymore.
|
||||||
|
//TODO: We should send a mining job here. For now we send it before the auth has taken place but.....
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSubmit(message){
|
||||||
|
if (!_this.authorized){
|
||||||
|
sendJson({
|
||||||
|
id : message.id,
|
||||||
|
result: null,
|
||||||
|
error : [24, "unauthorized worker", null]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_this.extraNonce1){
|
||||||
|
sendJson({
|
||||||
|
id : message.id,
|
||||||
|
result: null,
|
||||||
|
error : [25, "not subscribed", null]
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_this.emit('submit',
|
||||||
|
{
|
||||||
|
name : message.params[0],
|
||||||
|
jobId : message.params[1],
|
||||||
|
extraNonce2 : message.params[2],
|
||||||
|
nTime : message.params[3],
|
||||||
|
nonce : message.params[4]
|
||||||
|
},
|
||||||
|
function(error, result){
|
||||||
|
sendJson({
|
||||||
|
id : message.id,
|
||||||
|
result : result,
|
||||||
|
error : error
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendJson(){
|
||||||
|
var response = '';
|
||||||
|
for (var i = 0; i < arguments.length; i++){
|
||||||
|
response += JSON.stringify(arguments[i]) + '\n';
|
||||||
|
}
|
||||||
|
options.socket.write(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupSocket(){
|
||||||
|
var socket = options.socket;
|
||||||
|
var dataBuffer = '';
|
||||||
|
socket.setEncoding('utf8');
|
||||||
|
socket.on('data', function(d){
|
||||||
|
console.log('request: ' + d);
|
||||||
|
dataBuffer += d;
|
||||||
|
if (dataBuffer.slice(-1) === '\n'){
|
||||||
|
var messages = dataBuffer.split('\n');
|
||||||
|
messages.forEach(function(message){
|
||||||
|
if (message.trim() === '') return;
|
||||||
|
var messageJson;
|
||||||
|
try{
|
||||||
|
messageJson = JSON.parse(message);
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log('could not parse stratum client socket message: ' + message);
|
||||||
|
}
|
||||||
|
if (messageJson) {
|
||||||
|
handleMessage(messageJson);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dataBuffer = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
socket.on('end', function() {
|
||||||
|
_this.emit('socketDisconnect')
|
||||||
|
console.log('stratum client disconnected');
|
||||||
|
});
|
||||||
|
socket.on('error', function(){
|
||||||
|
_this.emit('socketError');
|
||||||
|
console.log('stratum client socket error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//public members
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IF the given difficulty is valid and new it'll send it to the client.
|
||||||
|
* returns boolean
|
||||||
|
**/
|
||||||
|
this.sendAndSetDifficultyIfNew = function(difficulty){
|
||||||
|
if (typeof(difficulty) != 'number') {
|
||||||
|
console.error('[StratumClient.sendAndSetDifficultyIfNew] given difficulty parameter is not a number: ['+difficulty+']');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difficulty !== this.difficulty) {
|
||||||
|
this.difficulty = difficulty;
|
||||||
|
sendJson({
|
||||||
|
id : null,
|
||||||
|
method: "mining.set_difficulty",
|
||||||
|
params: [difficulty]//[512],
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sendMiningJob = function(jobParams){
|
||||||
|
sendJson({
|
||||||
|
id : null,
|
||||||
|
method: "mining.notify",
|
||||||
|
params: jobParams
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
StratumClient.prototype.__proto__ = events.EventEmitter.prototype;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual stratum server.
|
||||||
|
* It emits the following Events:
|
||||||
|
* - 'client.connected'(StratumClientInstance) - when a new miner connects
|
||||||
|
* - 'client.disconnected'(StratumClientInstance) - when a miner disconnects. Be aware that the socket cannot be used anymore.
|
||||||
|
* - 'started' - when the server is up and running
|
||||||
|
**/
|
||||||
|
var StratumServer = exports.Server = function StratumServer(options){
|
||||||
|
|
||||||
|
//private members
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
var socketServer;
|
||||||
|
var stratumClients = {};
|
||||||
|
var subscriptionCounter = SubscriptionCounter();
|
||||||
|
|
||||||
|
(function init(){
|
||||||
|
_socketServer = socketServer = net.createServer(function(c){
|
||||||
|
var subscriptionId = subscriptionCounter.next();
|
||||||
|
var client = new StratumClient(
|
||||||
|
{
|
||||||
|
subscriptionId : subscriptionId,
|
||||||
|
socket : c,
|
||||||
|
authorizeFn : options.authorizeFn
|
||||||
|
}
|
||||||
|
);
|
||||||
|
stratumClients[subscriptionId] = client;
|
||||||
|
_this.emit('client.connected', client);
|
||||||
|
client.on('socketDisconnect', function() {
|
||||||
|
delete stratumClients[subscriptionId];
|
||||||
|
_this.emit('client.disconnected', client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_socketServer.listen(options.port, function(){
|
||||||
|
_this.emit('started');
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
//public members
|
||||||
|
|
||||||
|
this.broadcastMiningJobs = function(jobParams) {
|
||||||
|
for (var clientId in stratumClients) {
|
||||||
|
// if a client gets disconnected WHILE doing this loop a crash might happen.
|
||||||
|
// 'm not sure if that can ever happn but an if here doesn't hurt!
|
||||||
|
if (typeof(stratumClients[clientId]) !== 'undefined') {
|
||||||
|
stratumClients[clientId].sendMiningJob(jobParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
StratumServer.prototype.__proto__ = events.EventEmitter.prototype;
|
||||||
@ -86,23 +86,23 @@ var Generation = exports.Generation = function Generation(rpcData, publicKey, ex
|
|||||||
|
|
||||||
var tx = new Transaction({
|
var tx = new Transaction({
|
||||||
inputs: [new TransactionInput({
|
inputs: [new TransactionInput({
|
||||||
prevOutIndex: Math.pow(2, 32) - 1,
|
prevOutIndex : Math.pow(2, 32) - 1,
|
||||||
sigScript: new ScriptSig({
|
sigScript : new ScriptSig({
|
||||||
height: rpcData.height,
|
height : rpcData.height,
|
||||||
flags: rpcData.coinbaseaux.flags,
|
flags : rpcData.coinbaseaux.flags,
|
||||||
extraNoncePlaceholder: extraNoncePlaceholder
|
extraNoncePlaceholder : extraNoncePlaceholder
|
||||||
})
|
})
|
||||||
})],
|
})],
|
||||||
outputs: [new TransactionOutput({
|
outputs: [new TransactionOutput({
|
||||||
value: rpcData.coinbasevalue,
|
value : rpcData.coinbasevalue,
|
||||||
pkScriptBuffer: publicKey
|
pkScriptBuffer : publicKey
|
||||||
})]
|
})]
|
||||||
});
|
});
|
||||||
|
|
||||||
var txBuffer = tx.toBuffer();
|
var txBuffer = tx.toBuffer();
|
||||||
var epIndex = buffertools.indexOf(txBuffer, extraNoncePlaceholder);
|
var epIndex = buffertools.indexOf(txBuffer, extraNoncePlaceholder);
|
||||||
var p1 = txBuffer.slice(0, epIndex);
|
var p1 = txBuffer.slice(0, epIndex);
|
||||||
var p2 = txBuffer.slice(epIndex + extraNoncePlaceholder.length);
|
var p2 = txBuffer.slice(epIndex + extraNoncePlaceholder.length);
|
||||||
|
|
||||||
this.transaction = tx;
|
this.transaction = tx;
|
||||||
this.coinbase = [p1, p2];
|
this.coinbase = [p1, p2];
|
||||||
38
scripts/blockNotify.js
Normal file
38
scripts/blockNotify.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* This script should be hooked to the coin daemon as follow:
|
||||||
|
*
|
||||||
|
* litecoind -blocknotify="/path/to/this/script/blockNotify.js localhost:8117 password litecoin %s"
|
||||||
|
*
|
||||||
|
* The above will send tell litecoin to launch this script with those parameters every time
|
||||||
|
* a block is found.
|
||||||
|
* This script will then send the blockhash along with other informations to a listening tcp socket
|
||||||
|
**/
|
||||||
|
|
||||||
|
var net = require('net');
|
||||||
|
var config = process.argv[1];
|
||||||
|
var parts = config.split(':');
|
||||||
|
var host = parts[0];
|
||||||
|
var port = parts[1];
|
||||||
|
var password = process.argv[2];
|
||||||
|
var coin = process.argv[3];
|
||||||
|
var blockHash = process.argv[4];
|
||||||
|
|
||||||
|
var client = net.connect(port, host, function() {
|
||||||
|
console.log('client connected');
|
||||||
|
client.write(JSON.stringify({
|
||||||
|
password: password,
|
||||||
|
coin: coin,
|
||||||
|
blockHash: blockHash
|
||||||
|
}) + '\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('data', function(data) {
|
||||||
|
console.log(data.toString());
|
||||||
|
//client.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('end', function() {
|
||||||
|
console.log('client disconnected');
|
||||||
|
//process.exit();
|
||||||
|
});
|
||||||
229
stratum.js
229
stratum.js
@ -1,229 +0,0 @@
|
|||||||
var net = require('net');
|
|
||||||
var events = require('events');
|
|
||||||
|
|
||||||
var binpack = require('binpack');
|
|
||||||
|
|
||||||
var util = require('./util.js');
|
|
||||||
|
|
||||||
|
|
||||||
var SubscriptionCounter = function(){
|
|
||||||
var count = 0;
|
|
||||||
var padding = 'deadbeefcafebabe';
|
|
||||||
return {
|
|
||||||
next: function(){
|
|
||||||
count++;
|
|
||||||
if (Number.MAX_VALUE === count) count = 0;
|
|
||||||
return padding + binpack.packUInt64(count, 'big').toString('hex');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var StratumClient = function(options){
|
|
||||||
|
|
||||||
//private members
|
|
||||||
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
(function init(){
|
|
||||||
setupSocket();
|
|
||||||
})();
|
|
||||||
|
|
||||||
function handleMessage(message){
|
|
||||||
switch(message.method){
|
|
||||||
case 'mining.subscribe':
|
|
||||||
handleSubscribe(message);
|
|
||||||
break;
|
|
||||||
case 'mining.authorize':
|
|
||||||
handleAuthorize(message);
|
|
||||||
break;
|
|
||||||
case 'mining.submit':
|
|
||||||
handleSubmit(message);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.dir('unknown stratum client message: ' + message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubscribe(message){
|
|
||||||
_this.emit('subscription',
|
|
||||||
{},
|
|
||||||
function(error, extraNonce1, extraNonce2Size){
|
|
||||||
if (error){
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: null,
|
|
||||||
error: error
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_this.extraNonce1 = extraNonce1;
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: [
|
|
||||||
["mining.notify", options.subscriptionId],
|
|
||||||
extraNonce1,
|
|
||||||
extraNonce2Size
|
|
||||||
],
|
|
||||||
error: null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleAuthorize(message){
|
|
||||||
_this.emit('authorize',
|
|
||||||
{
|
|
||||||
name: message.params[0][0],
|
|
||||||
password: message.params[0][1]
|
|
||||||
},
|
|
||||||
function(error, result){
|
|
||||||
_this.authorized = result;
|
|
||||||
/*if (_this.authorized) {
|
|
||||||
// if authorized lets store the workername
|
|
||||||
// so that when a share is found we can get it for the accounting
|
|
||||||
_this.workerName = message.params[0][0];
|
|
||||||
}*/
|
|
||||||
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: result,
|
|
||||||
error: error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSubmit(message){
|
|
||||||
if (!_this.authorized){
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: null,
|
|
||||||
error: [24, "unauthorized worker", null]
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!_this.extraNonce1){
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: null,
|
|
||||||
error: [25, "not subscribed", null]
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log("SUBMIT "+JSON.stringify(message));
|
|
||||||
_this.emit('submit',
|
|
||||||
{
|
|
||||||
name: message.params[0],
|
|
||||||
jobId: message.params[1],
|
|
||||||
extraNonce2: message.params[2],
|
|
||||||
nTime: message.params[3],
|
|
||||||
nonce: message.params[4]
|
|
||||||
},
|
|
||||||
function(error, result){
|
|
||||||
sendJson({
|
|
||||||
id: message.id,
|
|
||||||
result: result,
|
|
||||||
error: error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendJson(){
|
|
||||||
var response = '';
|
|
||||||
for (var i = 0; i < arguments.length; i++){
|
|
||||||
response += JSON.stringify(arguments[i]) + '\n';
|
|
||||||
}
|
|
||||||
console.log('response: ' + response);
|
|
||||||
options.socket.write(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupSocket(){
|
|
||||||
var socket = options.socket;
|
|
||||||
var dataBuffer = '';
|
|
||||||
socket.setEncoding('utf8');
|
|
||||||
socket.on('data', function(d){
|
|
||||||
console.log('request: ' + d);
|
|
||||||
dataBuffer += d;
|
|
||||||
if (dataBuffer.slice(-1) === '\n'){
|
|
||||||
var messages = dataBuffer.split('\n');
|
|
||||||
messages.forEach(function(message){
|
|
||||||
if (message.trim() === '') return;
|
|
||||||
var messageJson;
|
|
||||||
try{
|
|
||||||
messageJson = JSON.parse(message);
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
console.log('could not parse stratum client socket message: ' + message);
|
|
||||||
}
|
|
||||||
if (messageJson)
|
|
||||||
handleMessage(messageJson);
|
|
||||||
});
|
|
||||||
dataBuffer = '';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
socket.on('end', function() {
|
|
||||||
_this.emit('socketDisconnect')
|
|
||||||
console.log('stratum client disconnected');
|
|
||||||
});
|
|
||||||
socket.on('error', function(){
|
|
||||||
_this.emit('socketError');
|
|
||||||
console.log('stratum client socket error');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//public members
|
|
||||||
|
|
||||||
this.sendDifficulty = function(difficulty){
|
|
||||||
_this.difficulty = difficulty;
|
|
||||||
sendJson({
|
|
||||||
id: null,
|
|
||||||
method: "mining.set_difficulty",
|
|
||||||
params: [difficulty]//[512],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
this.sendMiningJob = function(jobParams){
|
|
||||||
sendJson({
|
|
||||||
id: null,
|
|
||||||
method: "mining.notify",
|
|
||||||
params: jobParams
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
StratumClient.prototype.__proto__ = events.EventEmitter.prototype;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var StratumServer = exports.Server = function StratumServer(options){
|
|
||||||
|
|
||||||
//private members
|
|
||||||
|
|
||||||
var _this = this;
|
|
||||||
var socketServer;
|
|
||||||
var stratumClients = {};
|
|
||||||
var subscriptionCounter = SubscriptionCounter();
|
|
||||||
|
|
||||||
(function init(){
|
|
||||||
_socketServer = socketServer = net.createServer(function(c){
|
|
||||||
var subscriptionId = subscriptionCounter.next();
|
|
||||||
var client = new StratumClient({subscriptionId: subscriptionId, socket: c});
|
|
||||||
stratumClients[subscriptionId] = client;
|
|
||||||
_this.emit('client', client);
|
|
||||||
});
|
|
||||||
_socketServer.listen(options.port, function(){
|
|
||||||
_this.emit('started');
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
|
||||||
//public members
|
|
||||||
|
|
||||||
this.broadcastMiningJobs = function(jobParams){
|
|
||||||
for (var clientId in stratumClients){
|
|
||||||
stratumClients[clientId].sendMiningJob(jobParams)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
StratumServer.prototype.__proto__ = events.EventEmitter.prototype;
|
|
||||||
121
test.js
121
test.js
@ -1,121 +0,0 @@
|
|||||||
var bignum = require('bignum');
|
|
||||||
var scrypt = require('scrypt256-hash');
|
|
||||||
|
|
||||||
|
|
||||||
var reverseBuffer = function(buff){
|
|
||||||
var reversed = new Buffer(buff.length);
|
|
||||||
for (var i = buff.length - 1; i >= 0; i--)
|
|
||||||
reversed[buff.length - i - 1] = buff[i];
|
|
||||||
return reversed;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
var hash = new Buffer("38f3e68be0b74813af175b8da506dfa3c3017ff06fed7ae85e3efee655c9f7fd", 'hex');
|
|
||||||
var goal = "8be6f3381348b7e08d5b17afa3df06a5f07f01c3e87aed6fe6fe3e5efdf7c955";
|
|
||||||
|
|
||||||
var s = scrypt.digest(hash);
|
|
||||||
console.log(s.toString('hex'));
|
|
||||||
for (var i = 0; i < 20; i++) s.writeUInt32LE(s.readUInt32BE(i * 4), i * 4);
|
|
||||||
|
|
||||||
|
|
||||||
var nHash = new Buffer(hash.length);
|
|
||||||
for (var i = 0; i < 8; i++) nHash.writeUInt32LE(hash.readUInt32BE(i * 4), i * 4);
|
|
||||||
console.log('maybe: ' + nHash.toString('hex'));
|
|
||||||
|
|
||||||
|
|
||||||
var wow = bignum.fromBuffer(hash, {endian: 'little', size: 32}).toBuffer({endian: 'big', size: 32}).toString('hex');
|
|
||||||
console.log(wow);
|
|
||||||
console.log(wow == goal ? 'good' : 'fuck');
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
var bb = new Buffer('0100000017a93c491d6e309cc53604cc32829a9610a95835e042f7c86a0b4455b8f5fbfe38f3e68be0b74813af175b8da506dfa3c3017ff06fed7ae85e3efee655c9f7fdb931ce520334011c000028cf', 'hex');
|
|
||||||
var hash = scrypt.digest(bb);
|
|
||||||
console.log(bignum.fromBuffer(hash, {endian: 'little', size: 32}).toString());
|
|
||||||
*?
|
|
||||||
|
|
||||||
/*
|
|
||||||
var block = {
|
|
||||||
hash: "409fd235e2fdc7182db92e13eed1b352081d9013ddc90e0acd817e378b8c1d1a",
|
|
||||||
confirmations: 1,
|
|
||||||
size: 1913,
|
|
||||||
height: 493856,
|
|
||||||
version: 1,
|
|
||||||
merkleroot: "71669b50622da76f4d6940912e997b537006324e8a32ed06e8072c68abcd358f",
|
|
||||||
tx: [
|
|
||||||
"0d93c30ddc4b4802ec4724b238d730b7084ba19456ca5eeda8add1e4f86afab0",
|
|
||||||
"79f0461b1b2596381eff80cc1b4929eb908d6f6f010f082d7f4762b8d8e8c573",
|
|
||||||
"91b8d68ff49310174ceeefb2bb13765552676028108119ef2f8e5b29c8ed2487",
|
|
||||||
"c02a588d2296c13d1d6aec449af916a966880b6f359d841e3f09df554ade28ac",
|
|
||||||
"d043e490e4632fa48ac6fc3cbd6544e02116db39598aadb500fa7e3594bef9e3",
|
|
||||||
"f839b2688042cb5b6338f982a7c1179f0f533d1ca219aa12537acac0c1a3f863"
|
|
||||||
],
|
|
||||||
time: 1389202213,
|
|
||||||
nonce: 1370998784,
|
|
||||||
bits: "1d011a75",
|
|
||||||
difficulty: 0.90631872,
|
|
||||||
previousblockhash: "be11244bda34c4c08c23fa7c61a5445f6daab25049a2c75b91309b58a68d9083"
|
|
||||||
};
|
|
||||||
|
|
||||||
var phpResult = "0100000083908da6589b30915bc7a24950b2aa6d5f44a5617cfa238cc0c434da4b2411be8f35cdab682c07e806ed328a4e320670537b992e9140694d6fa72d62509b6671258bcd52751a011d00c8b751";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var header = new Buffer(80);
|
|
||||||
var position = 0;
|
|
||||||
header.writeUInt32BE(block.nonce, position);
|
|
||||||
header.write(block.bits, position += 4, 4, 'hex');
|
|
||||||
header.writeUInt32BE(block.time, position += 4);
|
|
||||||
header.write(block.merkleroot, position += 4, 32, 'hex');
|
|
||||||
header.write(block.previousblockhash, position += 32, 32, 'hex');
|
|
||||||
header.writeUInt32BE(block.version, position += 32);
|
|
||||||
var header = reverseBuffer(header);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (phpResult === header.toString('hex'))
|
|
||||||
console.log('works!!!!!');
|
|
||||||
else
|
|
||||||
console.log('fuck');
|
|
||||||
*/
|
|
||||||
|
|
||||||
nonce = "cf280000";
|
|
||||||
bits = "1c013403";
|
|
||||||
time = "52ce31b9";
|
|
||||||
merkleroot = "38f3e68be0b74813af175b8da506dfa3c3017ff06fed7ae85e3efee655c9f7fd";
|
|
||||||
pbh = "fefbf5b855440b6ac8f742e03558a910969a8232cc0436c59c306e1d493ca917";
|
|
||||||
version = 1;
|
|
||||||
|
|
||||||
merkleroot = reverseBuffer(new Buffer(merkleroot, 'hex')).toString('hex');
|
|
||||||
|
|
||||||
var serializeHeader = function(){
|
|
||||||
var header = new Buffer(80);
|
|
||||||
var position = 0;
|
|
||||||
header.write(nonce, position, 4, 'hex');
|
|
||||||
header.write(bits, position += 4, 4, 'hex');
|
|
||||||
header.write(time, position += 4, 4, 'hex');
|
|
||||||
header.write(merkleroot, position += 4, 32, 'hex');
|
|
||||||
header.write(pbh, position += 32, 32, 'hex');
|
|
||||||
header.writeUInt32BE(version, position + 32);
|
|
||||||
var header = reverseBuffer(header);
|
|
||||||
return header;
|
|
||||||
};
|
|
||||||
|
|
||||||
var headerBuff = serializeHeader();
|
|
||||||
var headerHashed = scrypt.digest(headerBuff);
|
|
||||||
var hashInt = bignum.fromBuffer(headerHashed, {endian: 'little', size: 32});
|
|
||||||
|
|
||||||
|
|
||||||
//var diffDividend = bignum.fromBuffer(new Buffer('0000ffff00000000000000000000000000000000000000000000000000000000'), 'hex');
|
|
||||||
//var target = diffDividend.div(16)
|
|
||||||
|
|
||||||
var dividend = 0x0000ffff00000000000000000000000000000000000000000000000000000000;
|
|
||||||
var target = dividend / 16;
|
|
||||||
|
|
||||||
var big = bignum(target);
|
|
||||||
|
|
||||||
console.log('dividend ' + big.toString());
|
|
||||||
|
|
||||||
console.log('hash ' + hashInt.toString())
|
|
||||||
console.log('target ' + target.toString());
|
|
||||||
Loading…
Reference in New Issue
Block a user