Fixed generation transaction building, block header serialization, and difficulty comparing :D

This commit is contained in:
Matthew Little 2014-01-10 16:56:22 -05:00
parent 8cdf82eb9f
commit 84e8c45b43
7 changed files with 127 additions and 294 deletions

View File

@ -14,7 +14,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
function getMerkleHashes(steps){
return steps.map(function(step){
return util.reverseBuffer(step).toString('hex');
return step.toString('hex');
});
}
@ -31,7 +31,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
this.rpcData = rpcData;
this.jobId = jobId;
this.target = util.bignumFromBits(rpcData.bits);
this.previousHashBuffer = util.reverseHex(rpcData.previousblockhash);
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');
}));
@ -65,19 +65,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
header.writeUInt32BE(rpcData.version, position + 32);
var header = util.reverseBuffer(header);
var test = header.toString('hex');
return header;
/*return Buffer.concat([
binpack.packInt32(rpcData.version, 'big'),
this.previousHashBuffer,
merkleRootBuffer,
nTimeBuffer,
new Buffer(this.rpcData.bits, 'hex'),
nonceBuffer
]);*/
};
this.serializeBlock = function(header, coinbase){
@ -102,7 +90,7 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
if (!this.jobParams){
this.jobParams = [
this.jobId,
this.previousHashBuffer,
this.prevHashReversed,
this.generationTransaction.coinbase[0].toString('hex'),
this.generationTransaction.coinbase[1].toString('hex'),
this.merkleBranch,
@ -114,4 +102,8 @@ var BlockTemplate = module.exports = function BlockTemplate(jobId, rpcData, publ
}
return this.jobParams;
}
//this.jobParams = this.getJobParams();
//console.log(JSON.stringify(this.jobParams, null, ' ').replace(/\n/g ,''));
}

View File

@ -61,19 +61,38 @@ var JobManager = module.exports = function JobManager(options){
_this.emit('newBlock', blockTemplate);
}
var diffDividend = bignum.fromBuffer(new Buffer((function(){
var diffDividend = (function(){
switch(options.algorithm){
case 'sha256':
return '00000000ffff0000000000000000000000000000000000000000000000000000';
return 0x00000000ffff0000000000000000000000000000000000000000000000000000;
case 'scrypt':
case 'scrypt-jane':
return '0000ffff00000000000000000000000000000000000000000000000000000000';
return 0x0000ffff00000000000000000000000000000000000000000000000000000000;
case 'quark':
return '000000ffff000000000000000000000000000000000000000000000000000000'
return 0x000000ffff000000000000000000000000000000000000000000000000000000;
}
})(), 'hex'));
})();
var hashDigest = (function(){
switch(options.algorithm){
case 'sha256':
return function(){
return util.doublesha.apply(this, arguments);
}
case 'scrypt':
return function(){
return scrypt.digest.apply(this, arguments);
}
case 'scrypt-jane':
return function(){
return scryptJane.digest.apply(this, arguments);
}
case 'quark':
return function(){
return quark.digest.apply(this, arguments);
}
}
})();
//public members
@ -94,25 +113,20 @@ var JobManager = module.exports = function JobManager(options){
if (extraNonce2.length / 2 !== _this.extraNonce2Size)
return {error: [20, 'incorrect size of extranonce2', null]};
var job = jobs[jobId];
if (!job)
return {error: [21, 'job not found', null]};
if (nTime.length !== 8)
return {error: [20, 'incorrect size of ntime']};
var nTimeInt = parseInt(nTime, 16);
if (nTimeInt < job.rpcData.curtime || nTime > submitTime + 7200)
return {error: [20, 'ntime out of range', null]};
if (nonce.length !== 8)
return {error: [20, 'incorrect size of nonce']};
if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce))
return {error: [22, 'duplicate share', null]};
@ -124,25 +138,10 @@ var JobManager = module.exports = function JobManager(options){
var coinbaseHash = util.doublesha(coinbaseBuffer);
var merkleRoot = job.merkleTree.withFirst(coinbaseHash);
for (var i = 0; i < 8; i++) merkleRoot.writeUInt32LE(merkleRoot.readUInt32BE(i * 4), i * 4);
merkleRoot = util.reverseBuffer(merkleRoot).toString('hex');
var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce);
for (var i = 0; i < 20; i++) headerBuffer.writeUInt32LE(headerBuffer.readUInt32BE(i * 4), i * 4);
var headerHash = (function(){
switch(options.algorithm){
case 'sha256':
return util.doublesha(headerBuffer);
case 'scrypt':
return scrypt.digest(headerBuffer);
case 'scrypt-jane':
return scryptJane.digest(headerBuffer, nTimeInt);
case 'quark':
return quark.digest(headerBuffer);
}
})();
var headerHash = hashDigest(headerBuffer, nTimeInt);
var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32});
if (job.target.ge(headerBigNum)){
@ -150,10 +149,8 @@ var JobManager = module.exports = function JobManager(options){
_this.emit('blockFound', blockHex);
}
var targetUser = diffDividend.div(difficulty);
var targetUser = bignum(diffDividend / difficulty);
if (headerBigNum.gt(targetUser)){
console.log('target:' + targetUser.toString());
console.log('share:' + headerBigNum.toString());
return {error: [23, 'low difficulty share', null]};
}

View File

@ -16,8 +16,8 @@ var coins = [
symbol: 'doge',
algorithm: 'scrypt', //or sha256, scrypt-jane, quark
reward: 'POW', //or POS
address: 'D5uXR7F6bTCJKRZBqj1D4gyHF9MHAd5oNs',
stratumPort: 3333,
address: 'DDt79i6P3Wro3SD3HSnkRLpMgUGUGdiNhS',
stratumPort: 3334,
difficulty: 8,
daemon: {
bin: 'dogecoind',

View File

@ -1,5 +1,6 @@
var net = require('net');
var events = require('events');
var fs = require('fs');
var async = require('async');
@ -51,8 +52,10 @@ var pool = module.exports = function pool(coin){
console.log('getblocktemplate rpc error for ' + coin.options.name);
callback(error);
}
else
else{
//result = JSON.parse(fs.readFileSync('example.json'));
callback(null, result);
}
}
);
},
@ -95,9 +98,6 @@ var pool = module.exports = function pool(coin){
_this.jobManager.newTemplate(results.rpcTemplate, publicKeyBuffer);
//console.log(results.rpcTemplate);
//console.log(_this.jobManager.currentJob.getJobParams());
});
}).on('startFailed', function(){

25
test.js
View File

@ -9,6 +9,7 @@ var reverseBuffer = function(buff){
return reversed;
};
/*
var hash = new Buffer("38f3e68be0b74813af175b8da506dfa3c3017ff06fed7ae85e3efee655c9f7fd", 'hex');
var goal = "8be6f3381348b7e08d5b17afa3df06a5f07f01c3e87aed6fe6fe3e5efdf7c955";
@ -25,6 +26,7 @@ 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');
*/
/*
@ -88,7 +90,6 @@ 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');
@ -98,9 +99,23 @@ var serializeHeader = function(){
header.write(pbh, position += 32, 32, 'hex');
header.writeUInt32BE(version, position + 32);
var header = reverseBuffer(header);
var test = header.toString('hex');
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());

View File

@ -5,26 +5,12 @@ var util = require('./util.js');
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);
}
})();
var version = params.version || 1,
inputs = params.inputs || [],
outputs = params.outputs || [],
lockTime = params.lockTime || 0;
function fromRaw(raw){
}
this.toBuffer = function(){
return Buffer.concat([
@ -43,28 +29,15 @@ function Transaction(params){
}
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);
}
})();
var prevOutHash = params.prevOutHash || 0,
prevOutIndex = params.prevOutIndex,
sigScript = params.sigScript,
sequence = params.sequence || 0;
function fromRaw(raw){
}
this.toBuffer = function(){
sigScriptBuffer = sigScript.toBuffer();
return Buffer.concat([
util.uint256BufferFromHash(prevOutHash),
binpack.packUInt32(prevOutIndex, 'little'),
@ -77,22 +50,8 @@ function TransactionInput(params){
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){
}
var value = params.value,
pkScriptBuffer = params.pkScriptBuffer;
this.toBuffer = function(){
return Buffer.concat([
@ -103,25 +62,36 @@ function TransactionOutput(params){
};
}
var buildScriptSig = function(height, flags, extraNoncePlaceholder){
return Buffer.concat([
util.serializeNumber(height),
new Buffer(flags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([extraNoncePlaceholder.length]),
extraNoncePlaceholder,
util.serializeString('/nodeStratum/')
]);
function ScriptSig(params){
var height = params.height,
flags = params.flags,
extraNoncePlaceholder = params.extraNoncePlaceholder;
this.toBuffer = function(){
return Buffer.concat([
util.serializeNumber(height),
new Buffer(flags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([extraNoncePlaceholder.length]),
extraNoncePlaceholder,
util.serializeString('/nodeStratum/')
]);
}
};
var Generation = exports.Generation = function Generation(rpcData, publicKey, extraNoncePlaceholder){
var scriptSig = buildScriptSig(rpcData.height, rpcData.coinbaseaux.flags, extraNoncePlaceholder);
var tx = new Transaction({
inputs: [new TransactionInput({
prevOutIndex: Math.pow(2, 32) - 1,
sigScriptBuffer: scriptSig
sigScript: new ScriptSig({
height: rpcData.height,
flags: rpcData.coinbaseaux.flags,
extraNoncePlaceholder: extraNoncePlaceholder
})
})],
outputs: [new TransactionOutput({
value: rpcData.coinbasevalue,
@ -137,150 +107,4 @@ var Generation = exports.Generation = function Generation(rpcData, publicKey, ex
this.transaction = tx;
this.coinbase = [p1, p2];
};
/*
function COutPoint(){
this.hash = 0;
this.n = 0;
}
COutPoint.prototype = {
deserialize: function(f){
this.hash = util.hexFromReversedBuffer(f.read(32));
this.n = f.read(4).readUInt32LE(0);
},
serialize: function(){
return Buffer.concat([
util.uint256BufferFromHash(this.hash),
binpack.packUInt32(this.n, 'little')
]);
}
};
function CTxIn(){
this.prevout = new COutPoint();
this.scriptSig = "";
this.nSequence = 0;
}
CTxIn.prototype = {
deserialize: function(f){
this.prevout = new COutPoint();
this.prevout.deserialize(f);
this.scriptSig = util.deser_string(f);
this.nSequence = f.read(4).readUInt32LE(0);
},
serialize: function(){
return Buffer.concat([
this.prevout.serialize(),
util.ser_string(this.scriptSig),
binpack.packUInt32(this.nSequence, 'little')
]);
}
};
function CTxOut(){
this.nValue = 0;
this.scriptPubKey = '';
}
CTxOut.prototype = {
deserialize: function(f){
this.nValue = f.read(8).readInt64LE(0);
this.scriptPubKey = util.deser_string(f);
},
serialize: function(){
return Buffer.concat([
binpack.packInt64(this.nValue, 'little'),
util.ser_string(this.scriptPubKey)
]);
}
};
function CTransaction(){
this.nVersion = 1;
this.vin = [];
this.vout = [];
this.nLockTime = 0;
this.sha256 = null;
};
CTransaction.prototype = {
deserialize: function(f){
util.makeBufferReadable(f);
this.nVersion = f.read(4).readInt32LE(0);
this.vin = util.deser_vector(f, CTxIn);
this.vout = util.deser_vector(f, CTxOut);
this.nLockTime = r.read(4).readUInt32LE(0);
this.sha256 = null;
},
serialize: function(){
return Buffer.concat([
binpack.packInt32(this.nVersion, 'little'),
util.ser_vector(this.vin),
util.ser_vector(this.vout),
binpack.packUInt32(this.nLockTime, 'little')
]);
}
};
exports.CTransaction = CTransaction;
var Generation = exports.Generation = function Generation(coinbaseValue, coinbaseAuxFlags, height, address){
var CTrans = new CTransaction();
var tx_in = new CTxIn();
tx_in.prevout.hash = 0;
tx_in.prevout.n = Math.pow(2, 32) - 1;
tx_in._scriptSig_template = [
Buffer.concat([
util.serializeNumber(height),
new Buffer(coinbaseAuxFlags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([exports.extranonce_size])
]),
util.ser_string('/stratum/')
];
tx_in.scriptSig = Buffer.concat([
tx_in._scriptSig_template[0],
extranonce_placeholder,
tx_in._scriptSig_template[1]
]);
var tx_out = new CTxOut();
tx_out.nValue = coinbaseValue;
tx_out.scriptPubKey = util.script_to_address(address);
CTrans.vin.push(tx_in);
CTrans.vout.push(tx_out);
var cTransBin = CTrans.serialize();
var epIndex = buffertools.indexOf(cTransBin, extranonce_placeholder);
var p1 = cTransBin.slice(0, epIndex);
var p2 = cTransBin.slice(epIndex + extranonce_placeholder.length);
this.tx = CTrans;
this.serialized = [p1, p2];
}
Generation.prototype = {
setExtraNonce: function(extraNonce){
if (extraNonce.length != exports.extranonce_size){
throw "Incorrect extranonce size";
}
var part1 = this.tx.vin[0]._scriptSig_template[0];
var part2 = this.tx.vin[0]._scriptSig_template[1];
this.tx.vin[0].scriptSig = Buffer.concat([
part1,
extraNonce,
part2
]);
}
};
*/
};

59
util.js
View File

@ -46,6 +46,11 @@ exports.reverseHex = function(hex){
return exports.reverseBuffer(new Buffer(hex, 'hex')).toString('hex');
};
exports.reverseByteOrder = function(buff){
for (var i = 0; i < 8; i++) buff.writeUInt32LE(buff.readUInt32BE(i * 4), i * 4);
return exports.reverseBuffer(buff);
};
exports.uint256BufferFromHash = function(hex){
var fromHex = new Buffer(hex, 'hex');
@ -64,6 +69,29 @@ exports.hexFromReversedBuffer = function(buffer){
return exports.reverseBuffer(buffer).toString('hex');
};
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.serializeNumber = function(n){
if (n < 0xfd){
var buff = new Buffer(2);
@ -74,13 +102,13 @@ exports.serializeNumber = function(n){
else if (n <= 0xffff){
var buff = new Buffer(4);
buff[0] = 0x3;
buff.writeUInt16BE(n, 1);
buff.writeUInt16LE(n, 1);
return buff;
}
else if (n <= 0xffffffff){
var buff = new Buffer(6);
buff[0] = 0x5;
buff.writeUInt32BE(n, 1);
var buff = new Buffer(5);
buff[0] = 0x4;
buff.writeUInt32LE(n, 1);
return buff;
}
else{
@ -115,29 +143,6 @@ exports.serializeString = function(s){
]);
};
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.range = function(start, stop, step){
if (typeof stop === 'undefined'){
stop = start;