stratum-pool-sugarchain
Version:
High performance Sugarchain Stratum poolserver in Node.js
293 lines (220 loc) • 8.29 kB
JavaScript
var util = require('./util.js');
/*
function Transaction(params){
var version = params.version || 1,
inputs = params.inputs || [],
outputs = params.outputs || [],
lockTime = params.lockTime || 0;
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 = params.prevOutHash || 0,
prevOutIndex = params.prevOutIndex,
sigScript = params.sigScript,
sequence = params.sequence || 0;
this.toBuffer = function(){
sigScriptBuffer = sigScript.toBuffer();
console.log('scriptSig length ' + sigScriptBuffer.length);
return Buffer.concat([
util.uint256BufferFromHash(prevOutHash),
binpack.packUInt32(prevOutIndex, 'little'),
util.varIntBuffer(sigScriptBuffer.length),
sigScriptBuffer,
binpack.packUInt32(sequence)
]);
};
}
function TransactionOutput(params){
var value = params.value,
pkScriptBuffer = params.pkScriptBuffer;
this.toBuffer = function(){
return Buffer.concat([
binpack.packInt64(value, 'little'),
util.varIntBuffer(pkScriptBuffer.length),
pkScriptBuffer
]);
};
}
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 tx = new Transaction({
inputs: [new TransactionInput({
prevOutIndex : Math.pow(2, 32) - 1,
sigScript : new ScriptSig({
height : rpcData.height,
flags : rpcData.coinbaseaux.flags,
extraNoncePlaceholder : extraNoncePlaceholder
})
})],
outputs: [new TransactionOutput({
value : rpcData.coinbasevalue,
pkScriptBuffer : publicKey
})]
});
var txBuffer = tx.toBuffer();
var epIndex = buffertools.indexOf(txBuffer, extraNoncePlaceholder);
var p1 = txBuffer.slice(0, epIndex);
var p2 = txBuffer.slice(epIndex + extraNoncePlaceholder.length);
this.transaction = tx;
this.coinbase = [p1, p2];
};
*/
/*
^^^^ The above code was a bit slow. The below code is uglier but optimized.
*/
/*
This function creates the generation transaction that accepts the reward for
successfully mining a new block.
For some (probably outdated and incorrect) documentation about whats kinda going on here,
see: https://en.bitcoin.it/wiki/Protocol_specification#tx
*/
var generateOutputTransactions = function(poolRecipient, recipients, rpcData){
var reward = rpcData.coinbasevalue;
var rewardToPool = reward;
var txOutputBuffers = [];
/* Dash 12.1 */
if (rpcData.masternode && rpcData.superblock) {
if (rpcData.masternode.payee) {
var payeeReward = 0;
payeeReward = rpcData.masternode.amount;
reward -= payeeReward;
rewardToPool -= payeeReward;
var payeeScript = util.addressToScript(rpcData.masternode.payee);
txOutputBuffers.push(Buffer.concat([
util.packInt64LE(payeeReward),
util.varIntBuffer(payeeScript.length),
payeeScript
]));
} else if (rpcData.superblock.length > 0) {
for(var i in rpcData.superblock){
var payeeReward = 0;
payeeReward = rpcData.superblock[i].amount;
reward -= payeeReward;
rewardToPool -= payeeReward;
var payeeScript = util.addressToScript(rpcData.superblock[i].payee);
txOutputBuffers.push(Buffer.concat([
util.packInt64LE(payeeReward),
util.varIntBuffer(payeeScript.length),
payeeScript
]));
}
}
}
if (rpcData.payee) {
var payeeReward = 0;
if (rpcData.payee_amount) {
payeeReward = rpcData.payee_amount;
} else {
payeeReward = Math.ceil(reward / 5);
}
reward -= payeeReward;
rewardToPool -= payeeReward;
var payeeScript = util.addressToScript(rpcData.payee);
txOutputBuffers.push(Buffer.concat([
util.packInt64LE(payeeReward),
util.varIntBuffer(payeeScript.length),
payeeScript
]));
}
for (var i = 0; i < recipients.length; i++){
var recipientReward = Math.floor(recipients[i].percent * reward);
rewardToPool -= recipientReward;
txOutputBuffers.push(Buffer.concat([
util.packInt64LE(recipientReward),
util.varIntBuffer(recipients[i].script.length),
recipients[i].script
]));
}
txOutputBuffers.unshift(Buffer.concat([
util.packInt64LE(rewardToPool),
util.varIntBuffer(poolRecipient.length),
poolRecipient
]));
if (rpcData.default_witness_commitment !== undefined){
witness_commitment = new Buffer(rpcData.default_witness_commitment, 'hex');
txOutputBuffers.unshift(Buffer.concat([
util.packInt64LE(0),
util.varIntBuffer(witness_commitment.length),
witness_commitment
]));
}
return Buffer.concat([
util.varIntBuffer(txOutputBuffers.length),
Buffer.concat(txOutputBuffers)
]);
};
exports.CreateGeneration = function(rpcData, publicKey, extraNoncePlaceholder, reward, txMessages, recipients){
var txInputsCount = 1;
var txOutputsCount = 1;
var txVersion = txMessages === true ? 2 : 1;
var txLockTime = 0;
var txInPrevOutHash = "";
var txInPrevOutIndex = Math.pow(2, 32) - 1;
var txInSequence = 0;
//Only required for POS coins
var txTimestamp = reward === 'POS' ?
util.packUInt32LE(rpcData.curtime) : new Buffer([]);
//For coins that support/require transaction comments
var txComment = txMessages === true ?
util.serializeString('https://github.com/zone117x/node-stratum') :
new Buffer([]);
var scriptSigPart1 = Buffer.concat([
util.serializeNumber(rpcData.height),
new Buffer(rpcData.coinbaseaux.flags, 'hex'),
util.serializeNumber(Date.now() / 1000 | 0),
new Buffer([extraNoncePlaceholder.length])
]);
var scriptSigPart2 = util.serializeString('/nodeStratum/');
var p1 = Buffer.concat([
util.packUInt32LE(txVersion),
txTimestamp,
//transaction input
util.varIntBuffer(txInputsCount),
util.uint256BufferFromHash(txInPrevOutHash),
util.packUInt32LE(txInPrevOutIndex),
util.varIntBuffer(scriptSigPart1.length + extraNoncePlaceholder.length + scriptSigPart2.length),
scriptSigPart1
]);
/*
The generation transaction must be split at the extranonce (which located in the transaction input
scriptSig). Miners send us unique extranonces that we use to join the two parts in attempt to create
a valid share and/or block.
*/
var outputTransactions = generateOutputTransactions(publicKey, recipients, rpcData);
var p2 = Buffer.concat([
scriptSigPart2,
util.packUInt32LE(txInSequence),
//end transaction input
//transaction output
outputTransactions,
//end transaction ouput
util.packUInt32LE(txLockTime),
txComment
]);
return [p1, p2];
};