this.processShare = function(jobId, previousDifficulty, difficulty, extraNonce1, extraNonce2, nTime, nonce, ipAddress, port, workerName){ var shareError = function(error){ _this.emit('share', { job: jobId, ip: ipAddress, worker: workerName, difficulty: difficulty, error: error[1] }); return {error: error, result: null}; }; var submitTime = Date.now() / 1000 | 0; if (extraNonce2.length / 2 !== _this.extraNonce2Size) return shareError([20, 'incorrect size of extranonce2']); var job = this.validJobs[jobId]; if (typeof job === 'undefined' || job.jobId != jobId ) { return shareError([21, 'job not found']); } if (nTime.length !== 8) { return shareError([20, 'incorrect size of ntime']); } var nTimeInt = parseInt(nTime, 16); if (nTimeInt < job.rpcData.curtime || nTimeInt > submitTime + 7200) { return shareError([20, 'ntime out of range']); } if (nonce.length !== 8) { return shareError([20, 'incorrect size of nonce']); } if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce)) { return shareError([22, 'duplicate share']); } var extraNonce1Buffer = new Buffer(extraNonce1, 'hex'); var extraNonce2Buffer = new Buffer(extraNonce2, 'hex'); var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer); var coinbaseHash = coinbaseHasher(coinbaseBuffer); var merkleRoot = util.reverseBuffer(job.merkleTree.withFirst(coinbaseHash)).toString('hex'); var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce); var headerHash = hashDigest(headerBuffer, nTimeInt); var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32}); var blockHashInvalid; var blockHash; var blockHex; var shareDiff = diff1 / headerBigNum.toNumber() * shareMultiplier; var blockDiffAdjusted = job.difficulty * shareMultiplier; //Check if share is a block candidate (matched network difficulty) if (job.target.ge(headerBigNum)){ blockHex = job.serializeBlock(headerBuffer, coinbaseBuffer).toString('hex'); if (options.coin.algorithm === 'neoscrypt') { blockHash = util.reverseBuffer(util.sha256d(headerBuffer, nTime)).toString('hex'); } else { blockHash = blockHasher(headerBuffer, nTime).toString('hex'); } } else { if (options.emitInvalidBlockHashes) blockHashInvalid = util.reverseBuffer(util.sha256d(headerBuffer)).toString('hex'); //Check if share didn't reached the miner's difficulty) if (shareDiff / difficulty < 0.99){ //Check if share matched a previous difficulty from before a vardiff retarget if (previousDifficulty && shareDiff >= previousDifficulty){ difficulty = previousDifficulty; } else{ return shareError([23, 'low difficulty share of ' + shareDiff]); } } } _this.emit('share', { job: jobId, ip: ipAddress, port: port, worker: workerName, height: job.rpcData.height, blockReward: job.rpcData.coinbasevalue, difficulty: difficulty, shareDiff: shareDiff.toFixed(8), blockDiff : blockDiffAdjusted, blockDiffActual: job.difficulty, blockHash: blockHash, blockHashInvalid: blockHashInvalid }, blockHex); return {result: true, error: null, blockHash: blockHash}; };
BigInteger.fromBase64 = function(b64_string) { var bi = bigint.fromBuffer(new Buffer(b64_string, 'base64')); return BigInteger._from_bigint(bi); };
this.processShare = function(jobId, previousDifficulty, difficulty, extraNonce1, extraNonce2, nTime, nonce, ipAddress, workerName){ var shareError = function(error){ _this.emit('share', { job: jobId, ip: ipAddress, worker: workerName, difficulty: difficulty, error: error[1] }); return {error: error, result: null}; }; var submitTime = Date.now() / 1000 | 0; if (extraNonce2.length / 2 !== _this.extraNonce2Size) return shareError([20, 'incorrect size of extranonce2']); var job = this.validJobs[jobId]; if (typeof job === 'undefined' || job.jobId != jobId ) { return shareError([21, 'job not found']); } if (nTime.length !== 8) { return shareError([20, 'incorrect size of ntime']); } var nTimeInt = parseInt(nTime, 16); if (nTimeInt < job.rpcData.curtime || nTimeInt > submitTime + 7200) { return shareError([20, 'ntime out of range']); } if (nonce.length !== 8) { return shareError([20, 'incorrect size of nonce']); } if (!job.registerSubmit(extraNonce1, extraNonce2, nTime, nonce)) { return shareError([22, 'duplicate share']); } var extraNonce1Buffer = new Buffer(extraNonce1, 'hex'); var extraNonce2Buffer = new Buffer(extraNonce2, 'hex'); var coinbaseBuffer = job.serializeCoinbase(extraNonce1Buffer, extraNonce2Buffer); var coinbaseHash = coinbaseHasher(coinbaseBuffer); var merkleRoot = util.reverseBuffer(job.merkleTree.withFirst(coinbaseHash)).toString('hex'); var headerBuffer = job.serializeHeader(merkleRoot, nTime, nonce); var headerHash = hashDigest(headerBuffer, nTimeInt); var headerBigNum = bignum.fromBuffer(headerHash, {endian: 'little', size: 32}); var blockHashInvalid; var blockHash; var blockHex; var shareDiff = maxDifficulty.div(headerBigNum); //Check if share is a block candidate (matched network difficulty) if (job.target.ge(headerBigNum)){ blockHex = job.serializeBlock(headerBuffer, coinbaseBuffer).toString('hex'); blockHash = util.reverseBuffer(util.sha256d(headerBuffer)).toString('hex'); } else { if (options.emitInvalidBlockHashes) blockHashInvalid = util.reverseBuffer(util.sha256d(headerBuffer)).toString('hex'); //Difficulty the miner is set to var targetUser = maxDifficulty.div(difficulty); //Check if share didn't reached the miner's difficulty) if (headerBigNum.gt(targetUser)){ //Check if share matched a previous difficulty from before a vardiff retarget if (previousDifficulty && !headerBigNum.gt(maxDifficulty.div(previousDifficulty))){ difficulty = previousDifficulty; } else{ var offPercent = 100 - (shareDiff.toNumber() / difficulty) * 100; //Check to see if low diff share is within acceptable configured range if (offPercent > (options.shareVariancePercent || 0)){ return shareError([23, 'low difficulty share of ' + shareDiff.toString()]); } else{ _this.emit('log', 'warning', 'Share accepted a low diff ' + shareDiff + ' off by ' + offPercent.toFixed(2) + '%'); } } } } _this.emit('share', { job: jobId, ip: ipAddress, worker: workerName, difficulty: difficulty, shareDiff: shareDiff, height: job.rpcData.height, reward: job.rpcData.coinbasevalue, networkDifficulty : job.difficulty.toString(), blockHash: blockHash, blockHashInvalid: blockHashInvalid }, blockHex); return {result: true, error: null, blockHash: blockHash}; };
exports.encryptTweet = function(user, tweet) { var message = new Buffer(tweet.tweet); tweet.tweet = exports.encryptWithUser(user, bignum.fromBuffer(message)); return tweet; }
function processShare(miner, job, blockTemplate, nonce, resultHash){ var template = new Buffer(blockTemplate.buffer.length); blockTemplate.buffer.copy(template); template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); var shareBuffer = cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex')); var convertedBlob; var hash; var shareType; if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability){ hash = new Buffer(resultHash, 'hex'); shareType = 'trusted'; } else { convertedBlob = cnUtil.convert_blob(shareBuffer); hash = cryptoNight(convertedBlob); shareType = 'valid'; } if (hash.toString('hex') !== resultHash) { log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); return false; } var hashArray = hash.toJSON(); hashArray.reverse(); var hashNum = bignum.fromBuffer(new Buffer(hashArray)); var hashDiff = diff1.div(hashNum); if (hashDiff.ge(blockTemplate.difficulty)){ apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function(error, result){ if (error){ log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } else{ var blockFastHash = cnUtil.get_block_id(shareBuffer).toString('hex'); log('info', logSystem, 'Block %s found at height %d by miner %s@%s - submit result: %j', [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] ); recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate); jobRefresh(); } }); } else if (hashDiff.lt(job.difficulty)){ log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); return false; } else{ recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } return true; }
state.get(address, function(err, data) { var account = new Account(data); account.balance = bignum.fromBuffer(account.balance).sub(minerReward).toBuffer(); state.put(address, account.serialize(), done); });
exports.str_to_num = function (str) { return bignum.fromBuffer(new Buffer(str)); };
var xor = function(buf, buf2){ return bignum.fromBuffer(buf).xor(bignum.fromBuffer(buf2)).toBuffer() }
HierarchicalKey.prototype.deriveChild = function(i) { var ib = []; ib.push((i >> 24) & 0xff); ib.push((i >> 16) & 0xff); ib.push((i >> 8) & 0xff); ib.push(i & 0xff); ib = new Buffer(ib); var usePrivate = (i & 0x80000000) != 0; var isPrivate = (this.version == networks['livenet'].hkeyPrivateVersion || this.version == networks['testnet'].hkeyPrivateVersion); if (usePrivate && (!this.hasPrivateKey || !isPrivate)) throw new Error('Cannot do private key derivation without private key'); var ret = null; if (this.hasPrivateKey) { var data = null; if (usePrivate) { data = Buffer.concat([new Buffer([0]), this.eckey.private, ib]); } else { data = Buffer.concat([this.eckey.public, ib]); } var hash = coinUtil.sha512hmac(data, this.chainCode); var il = bignum.fromBuffer(hash.slice(0, 32), { size: 32 }); var ir = hash.slice(32, 64); // ki = IL + kpar (mod n). var priv = bignum.fromBuffer(this.eckey.private, { size: 32 }); var k = il.add(priv).mod(secp256k1_n); ret = new HierarchicalKey(null); ret.chainCode = ir; ret.eckey = new Key(); ret.eckey.private = k.toBuffer({ size: 32 }); ret.eckey.regenerateSync(); ret.hasPrivateKey = true; } else { var data = Buffer.concat([this.eckey.public, ib]); var hash = coinUtil.sha512hmac(data, this.chainCode); var il = hash.slice(0, 32); var ir = hash.slice(32, 64); // Ki = (IL + kpar)*G = IL*G + Kpar var ilGkey = new Key(); ilGkey.private = il; ilGkey.regenerateSync(); ilGkey.compressed = false; var ilG = Point.fromUncompressedPubKey(ilGkey.public); var oldkey = new Key(); oldkey.public = this.eckey.public; oldkey.compressed = false; var Kpar = Point.fromUncompressedPubKey(oldkey.public); var newpub = Point.add(ilG, Kpar).toUncompressedPubKey(); ret = new HierarchicalKey(null); ret.chainCode = new Buffer(ir); var eckey = new Key(); eckey.public = newpub; eckey.compressed = true; ret.eckey = eckey; ret.hasPrivateKey = false; } ret.childIndex = i; ret.parentFingerprint = this.pubKeyHash.slice(0, 4); ret.version = this.version; ret.depth = this.depth + 1; ret.eckey.compressed = true; ret.pubKeyHash = coinUtil.sha256ripe160(ret.eckey.public); ret.buildExtendedPublicKey(); ret.buildExtendedPrivateKey(); return ret; }
/* * calculate the SRP-6 multiplier * * params: * N (bignum) group parameter N * g (bignum) generator * alg (string) default = ALG * * returns: bignum */ function getk(N, g, alg) { return bignum.fromBuffer(getkBuffer(N.toBuffer(), g.toBuffer(), alg)); };
/* * Random scrambling parameter u * * params: * A (bignum) client ephemeral public key * B (bignum) server ephemeral public key * N (bignum) group parameter N */ function getu(A, B, N, alg) { return bignum.fromBuffer(getuBuffer(A.toBuffer(), B.toBuffer(), N.toBuffer(), alg)); };
/** * Process miner share data **/ function processShare(miner, job, blockTemplate, params){ var nonce = params.nonce; var resultHash = params.result; var template = new Buffer(blockTemplate.buffer.length); if (!miner.proxy) { blockTemplate.buffer.copy(template); template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); } else { blockTemplate.buffer.copy(template); template.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); template.writeUInt32BE(params.poolNonce, job.clientPoolLocation); template.writeUInt32BE(params.workerNonce, job.clientNonceLocation); } var shareBuffer = utils.cnUtil.construct_block_blob(template, new Buffer(nonce, 'hex'), 0); var hash; var shareType; if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability){ hash = new Buffer(resultHash, 'hex'); shareType = 'trusted'; } else { let convertedBlob = utils.cnUtil.convert_blob(shareBuffer, 0); let hard_fork_version = convertedBlob[0]; let variant = 0; if(hard_fork_version == 3){ variant = 1; } else if(hard_fork_version > 3){ variant = 2; } hash = cryptonight(convertedBlob,variant); shareType = 'valid'; // log('info', logSystem, 'Mining pool algorithm: %s variant %d, Hard fork version: %d', [cnAlgorithm, cnVariant, hard_fork_version]); } if (hash.toString('hex') !== resultHash) { log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); return false; } var hashArray = hash.toByteArray().reverse(); var hashNum = bignum.fromBuffer(new Buffer(hashArray)); var hashDiff = diff1.div(hashNum); if (hashDiff.ge(blockTemplate.difficulty)){ rpcDaemon.submitBlock([shareBuffer.toString('hex')], function(error, result){ if (error){ log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); recordShareData(miner, job, hashDiff.toString(), false, null, shareType); }else{ var blockFastHash = utils.cnUtil.get_block_id(shareBuffer, 0).toString('hex'); log('info', logSystem, 'Block %s found at height %d by miner %s@%s - submit result: %j', [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] ); recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate); } jobRefresh(); }); } else if (hashDiff.lt(job.difficulty)){ log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); return false; } else{ recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } return true; }
var BlockTemplate = module.exports = function BlockTemplate(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.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(//new transactions.Generation( 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, new Buffer(reward === 'POS' ? [0] : []) //POS coins require a zero byte appended to block which the daemon replaces with the signature ]); }; 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; }; };
function processShare(miner, job, blockTemplate, nonce, resultHash){ var shareBuffer = new Buffer(blockTemplate.buffer.length); blockTemplate.buffer.copy(shareBuffer); shareBuffer.writeUInt32BE(job.extraNonce, blockTemplate.reserveOffset); if (typeof(nonce) === 'number' && nonce % 1 === 0) { var nonceBuf = bignum(nonce, 10).toBuffer(); var bufReversed = new Buffer(nonceBuf.toJSON().reverse()); bufReversed.copy(shareBuffer, 1); } else { new Buffer(nonce, 'hex').copy(shareBuffer, 1); } //XMR FIXME: //new Buffer(nonce, 'hex').copy(shareBuffer, 39); var convertedBlob; var hash; var shareType; if (shareTrustEnabled && miner.trust.threshold <= 0 && miner.trust.penalty <= 0 && Math.random() > miner.trust.probability){ hash = new Buffer(resultHash, 'hex'); shareType = 'trusted'; //Fixme do i need this? log('debug', logSystem, 'Share Validator', 'Trusted share from miner ' + miner.login + '@' + miner.ip); } else { convertedBlob = convertBlockBlob(shareBuffer); //XMR FIXME: //hash = cryptoNight(convertedBlob); hash = multiHashing.boolberry(convertedBlob, scratchpad, job.height); shareType = 'valid'; } //BBR FIXME: if (hash.toString('hex') !== resultHash) { //log('warn', logSystem, 'Bad hash from miner %s@%s', [miner.login, miner.ip]); log('warn', logSystem, 'Bad hash from miner ' + miner.login + '@' + miner.ip + '\n scratchpadHeight.height=' + scratchpadHeight.height + ', job.height=' + job.height + '\n calculated hash: ' + hash.toString('hex') + ', transfered hash: ' + resultHash); return false; } var hashArray = hash.toJSON(); hashArray.reverse(); var hashNum = bignum.fromBuffer(new Buffer(hashArray)); var hashDiff = diff1.div(hashNum); if (hashDiff.ge(blockTemplate.difficulty)){ apiInterfaces.rpcDaemon('submitblock', [shareBuffer.toString('hex')], function(error, result){ if (error){ log('error', logSystem, 'Error submitting block at height %d from %s@%s, share type: "%s" - %j', [job.height, miner.login, miner.ip, shareType, error]); recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } else{ var blockFastHash = cryptoNightFast(convertedBlob || convertBlockBlob(shareBuffer)).toString('hex'); log('info', logSystem, 'Block %s found at height %d by miner %s@%s - submit result: %j', [blockFastHash.substr(0, 6), job.height, miner.login, miner.ip, result] ); recordShareData(miner, job, hashDiff.toString(), true, blockFastHash, shareType, blockTemplate); //XMR FIXME: //jobRefresh(); if(aliases_config && aliases_config.aliases_que && aliases_config.aliases_que.length > 0) { aliases_config.aliases_que.shift(); storeAliasesQue(); log('debug', logSystem, 'Alias que updated.'); } } }); } else if (hashDiff.lt(job.difficulty)){ log('warn', logSystem, 'Rejected low difficulty share of %s from %s@%s', [hashDiff.toString(), miner.login, miner.ip]); return false; } else{ recordShareData(miner, job, hashDiff.toString(), false, null, shareType); } return true; }