diff --git a/app/controllers/blockchain.js b/app/controllers/blockchain.js index da748d54d01b1ae4a204ebe976b0664ca6a6118d..b3a44f530b4c7cef680d3a934433cc5e295fe716 100644 --- a/app/controllers/blockchain.js +++ b/app/controllers/blockchain.js @@ -90,10 +90,10 @@ function BlockchainBinding (server) { if (current) { nextBlockNumber = current ? current.number + 1 : 0; } - let nbZeros = yield globalValidator(conf, blockchainDao(null, server.dal)).getTrialLevel(idty.pubkey); + let difficulty = yield globalValidator(conf, blockchainDao(null, server.dal)).getTrialLevel(idty.pubkey); return { "block": nextBlockNumber, - "level": nbZeros + "level": difficulty }; }); @@ -104,10 +104,10 @@ function BlockchainBinding (server) { for (let i = 0, len = issuers.length; i < len; i++) { let issuer = issuers[i]; let member = yield server.dal.getWrittenIdtyByPubkey(issuer); - let level = yield globalValidator(conf, blockchainDao(null, server.dal)).getTrialLevel(member.pubkey); + let difficulty = yield globalValidator(conf, blockchainDao(null, server.dal)).getTrialLevel(member.pubkey); difficulties.push({ uid: member.uid, - level: level + level: difficulty }); } return { diff --git a/app/lib/constants.js b/app/lib/constants.js index ceda6b1557bd729f6a80aa99db150b501803c932..5ddba47fd615fb3589e46e803277c361c382b5fa 100644 --- a/app/lib/constants.js +++ b/app/lib/constants.js @@ -159,7 +159,13 @@ module.exports = { }, PROOF_OF_WORK: { EVALUATION: 200, - RELEASE_MEMORY: 10000 + RELEASE_MEMORY: 10000, + UPPER_BOUND: { + LEVEL_0: '9A-F', + LEVEL_1: '7', + LEVEL_2: '3', + LEVEL_3: '1' + } }, DURATIONS: { diff --git a/app/lib/globalValidator.js b/app/lib/globalValidator.js index 231a42b6b6a349f8ae2c4413fe199980ce7d8ece..22b8024e9c3a04109f9ba471be46059083ebe88d 100644 --- a/app/lib/globalValidator.js +++ b/app/lib/globalValidator.js @@ -8,6 +8,7 @@ var crypto = require('./crypto'); var moment = require('moment'); var util = require('util'); var stream = require('stream'); +var constants = require('./constants'); var Block = require('../lib/entity/block'); var Identity = require('../lib/entity/identity'); var Membership = require('../lib/entity/membership'); @@ -400,9 +401,9 @@ function GlobalValidator (conf, dao) { var lastDistant = yield dao.getBlock(Math.max(0, blockNumber - conf.dtDiffEval)); // Compute PoWMin value var duration = medianTime - lastDistant.medianTime; - var speed = (conf.dtDiffEval * 1.0) / (duration * 1.0); - var maxGenTime = conf.avgGenTime * 4; - var minGenTime = conf.avgGenTime / 4; + var speed = parseFloat(conf.dtDiffEval) / duration; + var maxGenTime = Math.ceil(conf.avgGenTime * Math.sqrt(2)); + var minGenTime = Math.floor(conf.avgGenTime / Math.sqrt(2)); var maxSpeed = 1.0 / minGenTime; var minSpeed = 1.0 / maxGenTime; // logger.debug('Current speed is', speed, '(' + conf.dtDiffEval + '/' + duration + ')', 'and must be [', minSpeed, ';', maxSpeed, ']'); @@ -430,14 +431,23 @@ function GlobalValidator (conf, dao) { function (next){ getTrialLevel(block.issuer, next); }, - function (nbZeros, next){ - var powRegexp = new RegExp('^0{' + nbZeros + ',}'); - if (!block.hash.match(powRegexp)) - next('Wrong proof-of-work level: given ' + block.hash.match(/^0*/)[0].length + ' zeros, required was ' + nbZeros + ' zeros'); + function (difficulty, next){ + var remainder = difficulty % 4; + var nbZerosReq = Math.max(0, (difficulty - remainder) / 4); + var highMark = remainder == 3 ? constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_3 + : (remainder == 2 ? constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_2 + : (remainder == 1 ? constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_1 + : constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_0)); + var powRegexp = new RegExp('^0{' + nbZerosReq + '}' + '[0-' + highMark + ']'); + if (!block.hash.match(powRegexp)) { + var givenZeros = Math.max(0, Math.min(nbZerosReq, block.hash.match(/^0*/)[0].length)); + var c = block.hash.substr(givenZeros, 1); + next('Wrong proof-of-work level: given ' + givenZeros + ' zeros and \'' + c + '\', required was ' + nbZerosReq + ' zeros and an hexa char between [0-' + highMark + ']'); + } else { next(); } - }, + } ], done); } @@ -656,8 +666,8 @@ function GlobalValidator (conf, dao) { function (issuers, next) { var nbPreviousIssuers = _(_(issuers).uniq()).without(issuer).length; var nbBlocksSince = current.number - last.number; - var nbZeros = Math.max(powMin, powMin * Math.floor(percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince))); - next(null, nbZeros); + var difficulty = Math.max(powMin, powMin * Math.floor(percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince))); + next(null, difficulty); } ], next); } diff --git a/app/lib/localValidator.js b/app/lib/localValidator.js index 3e61eecb5b29fb9940473e045f4fa77a00f1a136..3fde05fbad20b06da5c0122633de48b40e06d6a8 100644 --- a/app/lib/localValidator.js +++ b/app/lib/localValidator.js @@ -111,7 +111,7 @@ function LocalValidator (conf) { }; function maxAcceleration () { - return conf.avgGenTime * 4 * (Math.ceil((conf.medianTimeBlocks + 1) / 2)); + return Math.ceil(conf.avgGenTime * Math.sqrt(2)) * (Math.ceil((conf.medianTimeBlocks + 1) / 2) + 1); } this.checkSingleMembershipSignature = checkSingleMembershipSignature; @@ -130,7 +130,7 @@ function LocalValidator (conf) { }); this.checkProofOfWork = check(function (block, done) { - var powRegexp = new RegExp('^0{' + block.powMin + '}'); + var powRegexp = new RegExp('^0{' + Math.floor(block.powMin / 4) + '}'); if (!block.hash.match(powRegexp)) done('Not a proof-of-work'); else @@ -165,7 +165,6 @@ function LocalValidator (conf) { this.checkBlockTimes = check(function (block, done) { var time = parseInt(block.time); var medianTime = parseInt(block.medianTime); - var maxGenTime = conf.avgGenTime * 4; if (block.number > 0 && (time < medianTime || time > medianTime + maxAcceleration())) done('A block must have its Time between MedianTime and MedianTime + ' + maxAcceleration()); else if (block.number == 0 && time != medianTime) diff --git a/app/lib/proof.js b/app/lib/proof.js index 0f67316ff070eaa58b81aedc090af42ebff08a43..4ace9e8ceff501d1bb0ebd519537d4ef7c7b55e5 100644 --- a/app/lib/proof.js +++ b/app/lib/proof.js @@ -22,6 +22,7 @@ process.on('message', function(stuff){ var pair = stuff.pair; var forcedTime = stuff.forcedTime; var cpu = conf.cpu || 1; + var highMark = stuff.highMark; async.waterfall([ function(next) { if (signatureFunc) @@ -31,17 +32,17 @@ process.on('message', function(stuff){ }, function(sigFunc, next) { signatureFunc = sigFunc; - var powRegexp = new RegExp('^0{' + nbZeros + '}[^0]'); - var lowPowRegexp = new RegExp('^0{' + (nbZeros-1) + '}[^0]'); - var verylowPowRegexp = new RegExp('^0{' + (nbZeros-2) + '}[^0]'); + var powRegexp = new RegExp('^0{' + nbZeros + '}' + '[0-' + highMark + ']'); + var lowPowRegexp = new RegExp('^0{' + (nbZeros) + '}[^0]'); + var verylowPowRegexp = new RegExp('^0{' + (nbZeros - 1) + '}[^0]'); var pow = "", sig = "", raw = ""; // Time must be = [medianTime; medianTime + minSpeed] block.time = getBlockTime(block, conf, forcedTime); // Test CPU speed - var testsPerSecond = nbZeros > 0 ? computeSpeed(block, sigFunc) : 1; + var testsPerSecond = nbZeros == 0 && highMark == '9A-F' ? 1 : computeSpeed(block, sigFunc); var testsPerRound = Math.max(Math.round(testsPerSecond * cpu), 1); - process.send({ found: false, testsPerSecond: testsPerSecond, testsPerRound: testsPerRound }); + process.send({ found: false, testsPerSecond: testsPerSecond, testsPerRound: testsPerRound, nonce: block.nonce }); // Really start now var testsCount = 0; if (nbZeros == 0) { diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js index 76ba608d8181feb22e451cf02a6a81620c130a69..4269d6e8617274c240eaff9776d03717082d38d5 100644 --- a/app/service/BlockchainService.js +++ b/app/service/BlockchainService.js @@ -1044,7 +1044,14 @@ function BlockchainService (conf, mainDAL, pair) { } var powWorker; - this.prove = function (block, sigFunc, nbZeros, done, forcedTime) { + this.prove = function (block, sigFunc, difficulty, done, forcedTime) { + + var remainder = difficulty % 4; + var nbZeros = (difficulty - remainder) / 4; + var highMark = remainder == 3 ? constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_3 + : (remainder == 2 ? constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_2 + : (remainder == 1 ?constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_1 + : constants.PROOF_OF_WORK.UPPER_BOUND.LEVEL_0)); return Q.Promise(function(resolve, reject){ if (!powWorker) { @@ -1052,7 +1059,7 @@ function BlockchainService (conf, mainDAL, pair) { } if (block.number == 0) { // On initial block, difficulty is the one given manually - block.powMin = nbZeros; + block.powMin = difficulty; } // Start powWorker.setOnPoW(function(err, powBlock) { @@ -1066,12 +1073,12 @@ function BlockchainService (conf, mainDAL, pair) { }); block.nonce = 0; - powWorker.powProcess.send({ conf: conf, block: block, zeros: nbZeros, forcedTime: forcedTime, + powWorker.powProcess.send({ conf: conf, block: block, zeros: nbZeros, highMark: highMark, forcedTime: forcedTime, pair: { secretKeyEnc: base58.encode(pair.secretKey) } }); - logger.info('Generating proof-of-work of block #%s with %s leading zeros... (CPU usage set to %s%)', block.number, nbZeros, (conf.cpu*100).toFixed(0)); + logger.info('Generating proof-of-work with %s leading zeros followed by [0-' + highMark + ']... (CPU usage set to %s%)', nbZeros, (conf.cpu * 100).toFixed(0)); }); }; diff --git a/doc/Protocol.md b/doc/Protocol.md index b18541ff129c3b89bdc29bae93a82cae453abad9..eb1be7ea0e7914b939b5a07a3ad73476bf76fbd7 100644 --- a/doc/Protocol.md +++ b/doc/Protocol.md @@ -692,9 +692,9 @@ percentRot | The percent of previous issuers to reach for personalized difficul Variable | Meaning --------- | ---- members | Synonym of `members(t = now)`, `wot(t)`, `community(t)` targeting the keys whose last valid (non-expired) membership is either in `Joiners` or `Actives`. -maxGenTime | `= avgGenTime * 4` -minGenTime | `= avgGenTime / 4` -maxAcceleration | `= maxGenTime * CEIL((medianTimeBlocks + 1) / 2)` +maxGenTime | `= CEIL(avgGenTime * √2)` +minGenTime | `= FLOOR(avgGenTime / √2)` +maxAcceleration | `= maxGenTime * (CEIL((medianTimeBlocks + 1) / 2) + 1)` ## Processing diff --git a/test/data/blocks.js b/test/data/blocks.js index a72500fea7f54029991ec9e7abc0c7c11c459868..ac23f06e0cfbf111f9501b0c7a741605cc62f637 100644 --- a/test/data/blocks.js +++ b/test/data/blocks.js @@ -1636,7 +1636,7 @@ module.exports = { "Version: 1\n" + "Type: Block\n" + "Currency: beta_brousouf\n" + - "Nonce: 11\n" + + "Nonce: 12\n" + "Number: 3\n" + "PoWMin: 1\n" + "Time: 1411776000\n" + diff --git a/test/fast/block/block_global.js b/test/fast/block/block_global.js index 87e0b205bf5912f1143ec2017bbb37cce452e916..8c661a997710aa140ead45c52d694119985efe6e 100644 --- a/test/fast/block/block_global.js +++ b/test/fast/block/block_global.js @@ -189,17 +189,17 @@ describe("Block global coherence:", function(){ it('a block not starting with a leading zero should fail', test('checkProofOfWork', blocks.NO_LEADING_ZERO, function (err) { should.exist(err); - err.should.equal('Wrong proof-of-work level: given 0 zeros, required was 1 zeros'); + err.should.equal('Wrong proof-of-work level: given 0 zeros and \'C\', required was 0 zeros and an hexa char between [0-7]'); })); it('a block requiring 2 leading zeros but providing less should fail', test('checkProofOfWork', blocks.REQUIRES_7_LEADING_ZEROS, function (err) { should.exist(err); - err.should.equal('Wrong proof-of-work level: given 0 zeros, required was 2 zeros'); + err.should.equal('Wrong proof-of-work level: given 0 zeros and \'6\', required was 0 zeros and an hexa char between [0-3]'); })); it('a block requiring 1 leading zeros but providing less should fail', test('checkProofOfWork', blocks.REQUIRES_6_LEADING_ZEROS, function (err) { should.exist(err); - err.should.equal('Wrong proof-of-work level: given 0 zeros, required was 1 zeros'); + err.should.equal('Wrong proof-of-work level: given 0 zeros and \'C\', required was 0 zeros and an hexa char between [0-7]'); })); it('a block requiring 1 leading zeros as first block of newcomer should succeed', test('checkProofOfWork', blocks.FIRST_BLOCK_OF_NEWCOMER, function (err) { @@ -208,7 +208,7 @@ describe("Block global coherence:", function(){ it('a block requiring 40 leading zeros as second block of newcomer should fail', test('checkProofOfWork', blocks.SECOND_BLOCK_OF_NEWCOMER, function (err) { should.exist(err); - err.should.equal('Wrong proof-of-work level: given 0 zeros, required was 40 zeros'); + err.should.equal('Wrong proof-of-work level: given 0 zeros and \'6\', required was 10 zeros and an hexa char between [0-9A-F]'); })); it('a root block should not fail for time reason', test('checkTimes', blocks.WRONG_ROOT_DATES, function (err) { diff --git a/test/fast/block/block_local.js b/test/fast/block/block_local.js index 0ccfeffa8c49f584aff2fb9669d86e83f4b1e38b..3c0b8f3397d7fdf950f142508401f9d74ab3061e 100644 --- a/test/fast/block/block_local.js +++ b/test/fast/block/block_local.js @@ -10,12 +10,13 @@ var Block = require('../../../app/lib/entity/block'); var Configuration = require('../../../app/lib/entity/configuration'); var conf = Configuration.statics.complete({ - sigDelay: 365.25*24*3600, // 1 year + sigDelay: 365.25 * 24 * 3600, // 1 year sigQty: 1, powZeroMin: 1, powPeriod: 18, incDateMin: 10, avgGenTime: 60, + medianTimeBlocks: 20, dt: 100, ud0: 100, c: 0.1 @@ -76,12 +77,12 @@ describe("Block local coherence", function(){ })); it('a block with wrong date (in past)', test('checkBlockTimes', blocks.WRONG_DATE_LOWER, function (err, done) { - assert.equal(err, 'A block must have its Time between MedianTime and MedianTime + 2640'); + assert.equal(err, 'A block must have its Time between MedianTime and MedianTime + 1020'); done(); })); it('a block with wrong date (in future, but too far)', test('checkBlockTimes', blocks.WRONG_DATE_HIGHER_BUT_TOO_HIGH, function (err, done) { - assert.equal(err, 'A block must have its Time between MedianTime and MedianTime + 2640'); + assert.equal(err, 'A block must have its Time between MedianTime and MedianTime + 1020'); done(); }));