diff --git a/lib/local_comms.js b/lib/local_comms.js index b86bea4..dea4d04 100644 --- a/lib/local_comms.js +++ b/lib/local_comms.js @@ -517,38 +517,111 @@ function Database(){ txn.commit(); }; + this.getOldestLockedBlock = function(){ + /* + 6-29-2017 - Snipa - + This function returns a decompressed block proto for the first locked block in the system as part of the + share depth functions. DO NOT BLINDLY REPLACE getLastBlock WITH THIS FUNCTION. + */ + this.refreshEnv(); + debug("Getting the oldest locked block in the system"); + let txn = this.env.beginTxn({readOnly: true}); + let cursor = new this.lmdb.Cursor(txn, this.blockDB); + let highestBlock = null; + for (let found = cursor.goToFirst(); found; found = cursor.goToNext()) { + if (highestBlock !== null){ + break; + } + cursor.getCurrentBinary(function(key, data){ // jshint ignore:line + let blockData = global.protos.Block.decode(data); + if (blockData.unlocked === false){ + highestBlock = blockData; + } + }); + } + cursor.close(); + txn.commit(); + if (highestBlock !== null) { + debug("Got the oldest locked block in the system at height: " + JSON.stringify(highestBlock)); + } else { + debug("There are no unlocked blocks in the system. Woohoo!"); + } + return highestBlock; + }; + this.cleanShareDB = function() { /* - This function takes the difficulty of the current block, and the last PPS block. If it's 0, save everything, - UNLESS global.config.pps.enable is FALSE, then feel free to trash it. - Due to LMDB under current config, we must delete entire keys, due to this, we save diff * shareMultiLog * 1.5 - global.config.pplns.shareMultiLog should be at least 1.5x your shareMulti, in case of diff spikiing + This function takes the difficulty of the current block, and the last PPS block. If it's 0, save everything, + UNLESS global.config.pps.enable is FALSE, then feel free to trash it. + Due to LMDB under current config, we must delete entire keys, due to this, we save diff * shareMulti * 1.3 + 6/29/2017 - Fixed bug with the way the system got blocks. getLastBlock gets the most recent block. + getOldestLockedBlock gets the oldest block in the system that's locked. This ensures that shares for that block + can't be destroyed, and that there's enough depth past that point to ensure the system will have the ability + to make payouts based on the shareMultiLog. Thanks suhz for finding this. Sorry it hit your aeon pool hard. + :( -- Snipa + If current_height - global.payout.blocksRequired > lastLockedBlock, then set the scan start to + current_height - global.payout.blocksRequired - 1 so that we have the full block in case of PPS. + Otherwise, use the lastPPLNSBlock as the scan start. There we go. Stupid logic! + Math check! + cur_height = 100, blocksRequired=20, lastPPLNSLockedBlock.height=90 + In this case, the functional depth required for SOLO is 80 - 1, giving us 79 as our start + cur_height = 100, blocksRequired=20, lastPPLNSLockedBlock.height=70 + In this case, the PPLNS locked block is older than the current height - the required amount, so start is 70. + PPS height no longer matters! Yay! + Solo really doesn't matter, as block finder gets everything. + If there is no valid locked block to start from, aka all blocks are unlocked, then scan from the current height + of the chain, as there's no way for the system to have older blocks. We only need to save extra in the case + where there's unlocked blocks. A find on the current block will have enough depth as long as the saves are + correct. This will cause the system to clean up shares massively when there are no unlocked blocks. */ - let lastPPSBlock = this.getLastBlock(); - if (global.config.pps.enable){ - lastPPSBlock = this.getLastBlock(global.protos.POOLTYPE.PPS); - if (lastPPSBlock === 0){ - return; - } - } - let lastPPLNSBlock = this.getLastBlock(global.protos.POOLTYPE.PPLNS); - debug("Last PPS block: "+lastPPSBlock); - // Hopping into async, we need the current block height to know where to start our indexing... + let oldestLockedBlock = this.getOldestLockedBlock(); async.waterfall([ function(callback){ + global.coinFuncs.getBlockHeaderByHash(oldestLockedBlock.hash, (err, result) => { + oldestLockedBlock.height = result.height; + console.log(`Got the oldest block`); + callback(null, oldestLockedBlock); + }); + }, + function(oldestLockedBlock, callback){ global.coinFuncs.getLastBlockHeader(function(err, body){ - if (err) { - return callback(true, body); + if (oldestLockedBlock === null){ + /* + If there's no locked blocks, then allow the system to scan from the PPS depth downwards if PPS + is enabled. + Save enough shares so that the diff * share multi * 30% for buffer. + */ + if (global.config.pps.enable){ + // If PPS is enabled, we scan for new blocks at cur height - blocksRequired/2. + // We need to save shares back that far at the least. + callback(null, body.height - Math.floor(global.config.payout.blocksRequired/2), Math.floor(body.difficulty * global.config.pplns.shareMulti * 1.3)); + } else { + // Otherwise, we can just start from the current height. Woo! + callback(null, body.height, Math.floor(body.difficulty * global.config.pplns.shareMulti * 1.3)); + } + + } else { + /* + Otherwise, start the scan from the oldest locked block downwards. + This protects against the blockManager being messed up and not unlocking blocks. + This will ensure that enough shares are in place to unlock all blocks. + If the block is Solo, PPLNS or PPS, it doesn't matter. + */ + if (global.config.pps.enable && oldestLockedBlock.height > body.height - Math.floor(global.config.payout.blocksRequired/2)) { + // If PPS is enabled, and the oldestLockedBlock.height > the PPS minimum, start from the PPS minimum. + callback(null, body.height - Math.floor(global.config.payout.blocksRequired/2), Math.floor(body.difficulty * global.config.pplns.shareMulti * 1.3)); + } else { + // If PPS isn't enabled, or the oldestLockedBlock.height < the PPS minimum, then start from there. + callback(null, oldestLockedBlock.height, Math.floor(oldestLockedBlock.difficulty * global.config.pplns.shareMulti * 1.3)); + } } - return callback(null, body.height, Math.floor(body.difficulty * 1.5 * global.config.pplns.shareMultiLog)); }); }, function (lastBlock, difficulty, callback) { let shareCount = 0; - let ppsFound = false; let pplnsFound = false; let blockList = []; - debug("Scanning from: "+lastBlock + " for more than: " + difficulty + " shares"); + console.log("Scanning from: "+lastBlock + " for more than: " + difficulty + " shares"); range.range(0, lastBlock+1).forEach(function (blockID) { blockID = (blockID - lastBlock+1) * -1; if (blockID < 0){ @@ -558,7 +631,7 @@ function Database(){ let txn = global.database.env.beginTxn({readOnly: true}); let cursor = new global.database.lmdb.Cursor(txn, global.database.shareDB); for (let found = (cursor.goToRange(blockID) === blockID); found; found = cursor.goToNextDup()) { - if (ppsFound && pplnsFound){ + if (pplnsFound){ cursor.getCurrentBinary(function(key, data) { // jshint ignore:line if (blockList.indexOf(key) === -1){ blockList.push(key); @@ -566,9 +639,6 @@ function Database(){ }); } else { cursor.getCurrentBinary(function(key, data) { // jshint ignore:line - if (key < lastPPSBlock){ - ppsFound = true; - } try{ let shareData = global.protos.Share.decode(data); if (shareData.poolType === global.protos.POOLTYPE.PPLNS){ @@ -589,19 +659,11 @@ function Database(){ callback(null, blockList); } ], function(err, data){ - if (err === null && global.config.general.blockCleaner === true){ + if (global.config.general.blockCleaner === true){ if(data.length > 0){ global.database.refreshEnv(); - let blockList = global.database.getBlockList(); - debug("Got the block list"); let totalDeleted = 0; data.forEach(function(block){ - if ((blockList.indexOf(block) !== -1 && !blockList.unlocked) || block > lastPPLNSBlock){ - // Don't delete locked blocks. ffs. - // Don't delete blocks that could contain shares. Even if it's unlikely as all getout. - debug("Skipped deleting block: " + block); - return; - } totalDeleted += 1; let txn = global.database.env.beginTxn(); txn.del(global.database.shareDB, block); @@ -613,12 +675,11 @@ function Database(){ global.database.env.sync(function(){ }); } else { - console.log("Block cleaning disabled. Would of removed: " + JSON.stringify(data)); + console.log("Block cleaning disabled. Would have removed: " + JSON.stringify(data)); } }); }; - this.refreshEnv = function(){}; setInterval(function(){