node-stratum-pool/lib/blockTemplate.js

123 lines
3.9 KiB
JavaScript

var bignum = require('bignum');
var merkleTree = require('./merkleTree.js');
var transactions = require('./transactions.js');
var util = require('./util.js');
/**
* The BlockTemplate class holds a single job.
* and provides several methods to validate and submit it to the daemon coin
**/
var BlockTemplate = module.exports = function BlockTemplate(maxDifficulty, jobId, rpcData, publicKey, extraNoncePlaceholder, reward, txMessages){
//private members
var submits = [];
function getMerkleHashes(steps){
return steps.map(function(step){
return step.toString('hex');
});
}
function getTransactionBuffers(txs){
var txHashes = txs.map(function(tx){
return util.uint256BufferFromHash(tx.hash);
});
return [null].concat(txHashes);
}
//public members
this.rpcData = rpcData;
this.jobId = jobId;
/*
Use the 'target' field if available, but some daemons only return the 'bits' field
*/
this.target = rpcData.target ?
bignum.fromBuffer(new Buffer(rpcData.target, 'hex')) :
util.bignumFromBits(rpcData.bits);
this.difficulty = maxDifficulty.div(this.target).toNumber();
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');
}));
this.merkleTree = new merkleTree(getTransactionBuffers(rpcData.transactions));
this.merkleBranch = getMerkleHashes(this.merkleTree.steps);
this.generationTransaction = transactions.CreateGeneration(
rpcData,
publicKey,
extraNoncePlaceholder,
reward,
txMessages
);
this.serializeCoinbase = function(extraNonce1, extraNonce2){
return Buffer.concat([
this.generationTransaction[0],
extraNonce1,
extraNonce2,
this.generationTransaction[1]
]);
};
//https://en.bitcoin.it/wiki/Protocol_specification#Block_Headers
this.serializeHeader = function(merkleRoot, nTime, nonce){
var header = new Buffer(80);
var position = 0;
header.write(nonce, position, 4, 'hex');
header.write(rpcData.bits, position += 4, 4, 'hex');
header.write(nTime, position += 4, 4, 'hex');
header.write(merkleRoot, position += 4, 32, 'hex');
header.write(rpcData.previousblockhash, position += 32, 32, 'hex');
header.writeUInt32BE(rpcData.version, position + 32);
var header = util.reverseBuffer(header);
return header;
};
this.serializeBlock = function(header, coinbase){
return Buffer.concat([
header,
util.varIntBuffer(this.rpcData.transactions.length + 1),
coinbase,
this.transactionData,
//POS coins require a zero byte appended to block which the daemon replaces with the signature
new Buffer(reward === 'POS' ? [0] : [])
]);
};
this.registerSubmit = function(extraNonce1, extraNonce2, nTime, nonce){
var submission = extraNonce1 + extraNonce2 + nTime + nonce;
if (submits.indexOf(submission) === -1){
submits.push(submission);
return true;
}
return false;
};
this.getJobParams = function(){
if (!this.jobParams){
this.jobParams = [
this.jobId,
this.prevHashReversed,
this.generationTransaction[0].toString('hex'),
this.generationTransaction[1].toString('hex'),
this.merkleBranch,
util.packInt32BE(this.rpcData.version).toString('hex'),
this.rpcData.bits,
util.packUInt32BE(this.rpcData.curtime).toString('hex'),
true
];
}
return this.jobParams;
};
};