diff --git a/app/cli.js b/app/cli.js index 65c75fb4fd4877f4d25b398c8ff3834fb74b960d..0f31b5fbc833790fe6c8527d2ad32960ce209286 100644 --- a/app/cli.js +++ b/app/cli.js @@ -761,9 +761,9 @@ function service(callback, nologs) { return callback.apply(that, cbArgs); } catch (e) { server.disconnect(); - throw Error(err); + throw e; } - }); + }); }; } diff --git a/app/controllers/blockchain.js b/app/controllers/blockchain.js index 788a4653684560ca4c8695b38e74e5efb29cf44b..757bad9e2493dcd2d847373d31748a65e52817d3 100644 --- a/app/controllers/blockchain.js +++ b/app/controllers/blockchain.js @@ -89,7 +89,7 @@ function BlockchainBinding (server) { nextBlockNumber = current ? current.number + 1 : 0; } const version = current ? current.version : constants.BLOCK_GENERATED_VERSION; - const difficulty = yield rules.HELPERS.getTrialLevel(version, idty.pubkey, conf, server.dal); + const difficulty = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, idty.pubkey); return { "block": nextBlockNumber, "level": difficulty @@ -104,7 +104,7 @@ function BlockchainBinding (server) { const version = current ? current.version : constants.BLOCK_GENERATED_VERSION; for (const issuer of issuers) { const member = yield server.dal.getWrittenIdtyByPubkey(issuer); - const difficulty = yield rules.HELPERS.getTrialLevel(version, member.pubkey, conf, server.dal); + const difficulty = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, member.pubkey); difficulties.push({ uid: member.uid, level: difficulty diff --git a/app/controllers/wot.js b/app/controllers/wot.js index d984ef9c37a15e23250349f2be8b76dfdffcf2ed..fb8979a7a641e66dadedc14a8d6cc06e14d5c434 100644 --- a/app/controllers/wot.js +++ b/app/controllers/wot.js @@ -28,38 +28,33 @@ function WOTBinding (server) { // Entitify each result identities.forEach((idty, index) => identities[index] = new Identity(idty)); // Prepare some data to avoid displaying expired certifications - const excluding = yield BlockchainService.getCertificationsExludingBlock(); for (const idty of identities) { - const certs = yield server.dal.certsToTarget(idty.getTargetHash()); + const certs = yield server.dal.certsToTarget(idty.pubkey, idty.getTargetHash()); const validCerts = []; for (const cert of certs) { - if (!(excluding && cert.block <= excluding.number)) { - const member = yield IdentityService.getWrittenByPubkey(cert.from); - if (member) { - cert.uids = [member.uid]; - cert.isMember = member.member; - cert.wasMember = member.wasMember; - } else { - const potentials = yield IdentityService.getPendingFromPubkey(cert.from); - cert.uids = _(potentials).pluck('uid'); - cert.isMember = false; - cert.wasMember = false; - } - validCerts.push(cert); + const member = yield IdentityService.getWrittenByPubkey(cert.from); + if (member) { + cert.uids = [member.uid]; + cert.isMember = member.member; + cert.wasMember = member.wasMember; + } else { + const potentials = yield IdentityService.getPendingFromPubkey(cert.from); + cert.uids = _(potentials).pluck('uid'); + cert.isMember = false; + cert.wasMember = false; } + validCerts.push(cert); } idty.certs = validCerts; const signed = yield server.dal.certsFrom(idty.pubkey); const validSigned = []; for (let j = 0; j < signed.length; j++) { const cert = _.clone(signed[j]); - if (!(excluding && cert.block <= excluding.number)) { - cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target); - if (cert.idty) { - validSigned.push(cert); - } else { - logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to); - } + cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target); + if (cert.idty) { + validSigned.push(cert); + } else { + logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to); } } idty.signed = validSigned; @@ -99,27 +94,24 @@ function WOTBinding (server) { this.certifiersOf = (req) => co(function *() { const search = yield ParametersService.getSearchP(req); const idty = yield IdentityService.findMemberWithoutMemberships(search); - const excluding = yield BlockchainService.getCertificationsExludingBlock(); - const certs = yield server.dal.certsToTarget(idty.getTargetHash()); + const certs = yield server.dal.certsToTarget(idty.pubkey, idty.getTargetHash()); idty.certs = []; for (const cert of certs) { - if (!(excluding && cert.block <= excluding.number)) { - const certifier = yield server.dal.getWrittenIdtyByPubkey(cert.from); - if (certifier) { - cert.uid = certifier.uid; - cert.isMember = certifier.member; - cert.sigDate = certifier.buid; - cert.wasMember = true; // As we checked if(certified) - if (!cert.cert_time) { - // TODO: would be more efficient to save medianTime on certification reception - let certBlock = yield server.dal.getBlock(cert.block_number); - cert.cert_time = { - block: certBlock.number, - medianTime: certBlock.medianTime - }; - } - idty.certs.push(cert); + const certifier = yield server.dal.getWrittenIdtyByPubkey(cert.from); + if (certifier) { + cert.uid = certifier.uid; + cert.isMember = certifier.member; + cert.sigDate = certifier.buid; + cert.wasMember = true; // As we checked if(certified) + if (!cert.cert_time) { + // TODO: would be more efficient to save medianTime on certification reception + let certBlock = yield server.dal.getBlock(cert.block_number); + cert.cert_time = { + block: certBlock.number, + medianTime: certBlock.medianTime + }; } + idty.certs.push(cert); } } const json = { @@ -162,27 +154,24 @@ function WOTBinding (server) { this.certifiedBy = (req) => co(function *() { const search = yield ParametersService.getSearchP(req); const idty = yield IdentityService.findMemberWithoutMemberships(search); - const excluding = yield BlockchainService.getCertificationsExludingBlock(); const certs = yield server.dal.certsFrom(idty.pubkey); idty.certs = []; for (const cert of certs) { - if (!(excluding && cert.block <= excluding.number)) { - const certified = yield server.dal.getWrittenIdtyByPubkey(cert.to); - if (certified) { - cert.uid = certified.uid; - cert.isMember = certified.member; - cert.sigDate = certified.buid; - cert.wasMember = true; // As we checked if(certified) - if (!cert.cert_time) { - // TODO: would be more efficient to save medianTime on certification reception - let certBlock = yield server.dal.getBlock(cert.block_number); - cert.cert_time = { - block: certBlock.number, - medianTime: certBlock.medianTime - }; - } - idty.certs.push(cert); + const certified = yield server.dal.getWrittenIdtyByPubkey(cert.to); + if (certified) { + cert.uid = certified.uid; + cert.isMember = certified.member; + cert.sigDate = certified.buid; + cert.wasMember = true; // As we checked if(certified) + if (!cert.cert_time) { + // TODO: would be more efficient to save medianTime on certification reception (note: now partially done with INDEX) + let certBlock = yield server.dal.getBlock(cert.block_number); + cert.cert_time = { + block: certBlock.number, + medianTime: certBlock.medianTime + }; } + idty.certs.push(cert); } } const json = { diff --git a/app/lib/computation/blockGenerator.js b/app/lib/computation/blockGenerator.js index 8411a014a00eecb4b56312b14c8c2c143499ad1c..e087b3cee2f7410bcab7df1f734df2f9d91457e7 100644 --- a/app/lib/computation/blockGenerator.js +++ b/app/lib/computation/blockGenerator.js @@ -4,6 +4,7 @@ const co = require('co'); const Q = require('q'); const moment = require('moment'); const inquirer = require('inquirer'); +const indexer = require('../dup/indexer'); const rawer = require('../ucp/rawer'); const hashf = require('../ucp/hashf'); const constants = require('../constants'); @@ -33,13 +34,7 @@ function BlockGenerator(mainContext, prover) { logger = require('../logger')(dal.profile); }; - this.nextBlock = (manualValues) => generateNextBlock(new NextBlockGenerator(conf, dal), manualValues); - - this.nextEmptyBlock = () => co(function *() { - const current = yield dal.getCurrentBlockOrNull(); - const exclusions = yield dal.getToBeKickedPubkeys(); - return createBlock(current, {}, {}, {}, [], exclusions, []); - }); + this.nextBlock = (manualValues) => generateNextBlock(new NextBlockGenerator(mainContext, conf, dal), manualValues); this.manualRoot = () => co(function *() { let current = yield dal.getCurrentBlockOrNull(); @@ -53,7 +48,7 @@ function BlockGenerator(mainContext, prover) { const unsignedBlock = block || (yield that.nextBlock(manualValues)); const current = yield dal.getCurrentBlockOrNull(); const version = current ? current.version : constants.BLOCK_GENERATED_VERSION; - const trialLevel = trial || (yield rules.HELPERS.getTrialLevel(version, selfPubkey, conf, dal)); + const trialLevel = trial || (yield mainContext.getIssuerPersonalizedDifficulty(version, selfPubkey)); return prover.prove(unsignedBlock, trialLevel, (manualValues && manualValues.time) || null); }); @@ -151,7 +146,9 @@ function BlockGenerator(mainContext, prover) { block = {}; } const identity = yield dal.getIdentityByHashOrNull(leave.idHash); - if (identity && block && identity.currentMSN < leave.ms.number && identity.member) { + const currentMembership = yield dal.mindexDAL.getReducedMS(ms.issuer); + const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1; + if (identity && block && currentMSN < leave.ms.number && identity.member) { // MS + matching cert are found leave.identity = identity; leaveData[identity.pubkey] = leave; @@ -269,7 +266,9 @@ function BlockGenerator(mainContext, prover) { const idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase(); const join = yield that.getSinglePreJoinData(current, idtyHash, joiners); join.ms = ms; - if (!join.identity.revoked && join.identity.currentMSN < parseInt(join.ms.number)) { + const currentMembership = yield dal.mindexDAL.getReducedMS(ms.issuer); + const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1; + if (!join.identity.revoked && currentMSN < parseInt(join.ms.number)) { preJoinData[join.identity.pubkey] = join; } } catch (err) { @@ -328,7 +327,7 @@ function BlockGenerator(mainContext, prover) { this.getSinglePreJoinData = (current, idHash, joiners) => co(function *() { const identity = yield dal.getIdentityByHashOrNull(idHash); let foundCerts = []; - const blockOfChainability = current ? (yield dal.getChainabilityBlock(current.medianTime, conf.sigPeriod)) : null; + const vHEAD_1 = yield mainContext.getvHEAD_1(); if (!identity) { throw 'Identity with hash \'' + idHash + '\' not found'; } @@ -349,7 +348,8 @@ function BlockGenerator(mainContext, prover) { if (!verified) { throw constants.ERRORS.IDENTITY_WRONGLY_SIGNED; } - if (!identity.leaving) { + const isIdentityLeaving = yield dal.isLeaving(idty.pubkey); + if (!isIdentityLeaving) { if (!current) { // Look for certifications from initial joiners // TODO: check if this is still working @@ -375,12 +375,12 @@ function BlockGenerator(mainContext, prover) { } } // Already exists a link not replayable yet? - let exists = yield dal.existsLinkFromOrAfterDate(cert.from, cert.to, current.medianTime - conf.sigValidity); + let exists = yield dal.existsNonReplayableLink(cert.from, cert.to); if (exists) { throw 'It already exists a similar certification written, which is not replayable yet'; } // Already exists a link not chainable yet? - exists = yield dal.existsNonChainableLink(cert.from, blockOfChainability ? blockOfChainability.number : -1, conf.sigStock); + exists = yield dal.existsNonChainableLink(cert.from, vHEAD_1, conf.sigStock); if (exists) { throw 'It already exists a certification written which is not chainable yet'; } @@ -411,7 +411,9 @@ function BlockGenerator(mainContext, prover) { const createBlock = (current, joinData, leaveData, updates, revocations, exclusions, transactions, manualValues) => { return co(function *() { - const maxLenOfBlock = yield rules.HELPERS.getMaxBlockSize(dal); + const vHEAD = yield mainContext.getvHeadCopy(); + const vHEAD_1 = yield mainContext.getvHEAD_1(); + const maxLenOfBlock = indexer.DUP_HELPERS.getMaxBlockSize(vHEAD); let blockLen = 0; // Revocations have an impact on exclusions revocations.forEach((idty) => exclusions.push(idty.pubkey)); @@ -433,7 +435,7 @@ function BlockGenerator(mainContext, prover) { block.medianTime = moment.utc().unix() - conf.rootoffset; } else { - block.medianTime = yield rules.HELPERS.getMedianTime(block.number, conf, dal); + block.medianTime = vHEAD.medianTime; } // Choose the version block.version = (manualValues && manualValues.version) || (yield rules.HELPERS.getMaxPossibleVersionNumber(current, block)); @@ -552,6 +554,8 @@ function BlockGenerator(mainContext, prover) { // Final number of members block.membersCount = previousCount + block.joiners.length - block.excluded.length; + vHEAD.membersCount = block.membersCount; + /***** * Priority 4: transactions */ @@ -570,20 +574,26 @@ function BlockGenerator(mainContext, prover) { /** * Finally handle the Universal Dividend */ - block.powMin = block.number == 0 ? conf.powMin || 0 : yield rules.HELPERS.getPoWMin(block.version, block.number, conf, dal); + block.powMin = vHEAD.powMin; + + // BR_G13 + indexer.prepareDividend(vHEAD, vHEAD_1, conf); + + // BR_G14 + indexer.prepareUnitBase(vHEAD, vHEAD_1, conf); + // Universal Dividend - const nextUD = yield rules.HELPERS.getNextUD(dal, conf, block.version, block.medianTime, current, block.membersCount); - if (nextUD) { - block.dividend = nextUD.dividend; - block.unitbase = nextUD.unitbase; + if (vHEAD.new_dividend) { + block.dividend = vHEAD.dividend; + block.unitbase = vHEAD.unitBase; } else if (block.version > 2) { block.unitbase = block.number == 0 ? 0 : current.unitbase; } - // V3 Rotation + // Rotation if (block.version > 2) { - block.issuersCount = yield rules.HELPERS.getDifferentIssuers(dal); - block.issuersFrame = yield rules.HELPERS.getIssuersFrame(dal); - block.issuersFrameVar = yield rules.HELPERS.getIssuersFrameVar(block, dal); + block.issuersCount = vHEAD.issuersCount; + block.issuersFrame = vHEAD.issuersFrame; + block.issuersFrameVar = vHEAD.issuersFrameVar; } // InnerHash block.time = block.medianTime; @@ -600,7 +610,7 @@ function BlockGenerator(mainContext, prover) { * Class to implement strategy of automatic selection of incoming data for next block. * @constructor */ -function NextBlockGenerator(conf, dal) { +function NextBlockGenerator(mainContext, conf, dal) { const logger = require('../logger')(dal.profile); @@ -608,8 +618,7 @@ function NextBlockGenerator(conf, dal) { const updates = {}; const updatesToFrom = {}; const certs = yield dal.certsFindNew(); - // The block above which (above from current means blocks with number < current) - const blockOfChainability = current ? (yield dal.getChainabilityBlock(current.medianTime, conf.sigPeriod)) : null; + const vHEAD_1 = yield mainContext.getvHEAD_1(); for (const cert of certs) { const targetIdty = yield dal.getIdentityByHashOrNull(cert.target); // The identity must be known @@ -634,12 +643,12 @@ function NextBlockGenerator(conf, dal) { let exists = false; if (current) { // Already exists a link not replayable yet? - exists = yield dal.existsLinkFromOrAfterDate(cert.from, cert.to, current.medianTime - conf.sigValidity); + exists = yield dal.existsNonReplayableLink(cert.from, cert.to); } if (!exists) { // Already exists a link not chainable yet? // No chainability block means absolutely nobody can issue certifications yet - exists = current && (yield dal.existsNonChainableLink(cert.from, blockOfChainability ? blockOfChainability.number : -1, conf.sigStock)); + exists = yield dal.existsNonChainableLink(cert.from, vHEAD_1, conf.sigStock); if (!exists) { // It does NOT already exists a similar certification written, which is not replayable yet // Signatory must be a member diff --git a/app/lib/computation/blockchainContext.js b/app/lib/computation/blockchainContext.js index 2dd0bb208e2898b8ae8f1941820f92519e28316b..a607ee597911d6977bfeaf165f331e6f1abce2a2 100644 --- a/app/lib/computation/blockchainContext.js +++ b/app/lib/computation/blockchainContext.js @@ -2,6 +2,7 @@ const _ = require('underscore'); const co = require('co'); const Q = require('q'); +const indexer = require('../dup/indexer'); const hashf = require('../ucp/hashf'); const rawer = require('../ucp/rawer'); const constants = require('../constants'); @@ -10,8 +11,6 @@ const Identity = require('../entity/identity'); const Certification = require('../entity/certification'); const Membership = require('../entity/membership'); const Block = require('../entity/block'); -const Link = require('../entity/link'); -const Source = require('../entity/source'); const Transaction = require('../entity/transaction'); module.exports = () => { return new BlockchainContext() }; @@ -21,6 +20,84 @@ function BlockchainContext() { const that = this; let conf, dal, logger; + /** + * The virtual next HEAD. Computed each time a new block is added, because a lot of HEAD variables are deterministic + * and can be computed one, just after a block is added for later controls. + */ + let vHEAD; + + /** + * The currently written HEAD, aka. HEAD_1 relatively to incoming HEAD. + */ + let vHEAD_1; + + let HEADrefreshed = Q(); + + /** + * Refresh the virtual HEAD value for determined variables of the next coming block, avoiding to recompute them + * each time a new block arrives to check if the values are correct. We can know and store them early on, in vHEAD. + */ + function refreshHead() { + HEADrefreshed = co(function*() { + vHEAD_1 = yield dal.head(1); + // We suppose next block will have same version #, and no particular data in the block (empty index) + let block; + // But if no HEAD_1 exist, we must initialize a block with default values + if (!vHEAD_1) { + block = { + version: constants.BLOCK_GENERATED_VERSION, + time: Math.round(Date.now() / 1000), + powMin: conf.powMin || 0, + powZeros: 0, + powRemainder: 0, + avgBlockSize: 0 + }; + } else { + block = { version: vHEAD_1.version }; + } + vHEAD = yield indexer.completeGlobalScope(Block.statics.fromJSON(block).json(), conf, [], dal); + }); + return HEADrefreshed; + } + + /** + * Gets a copy of vHEAD, extended with some extra properties. + * @param props The extra properties to add. + */ + this.getvHeadCopy = (props) => co(function*() { + if (!vHEAD) { + yield refreshHead(); + } + const copy = {}; + const keys = Object.keys(vHEAD); + for (const k of keys) { + copy[k] = vHEAD[k]; + } + _.extend(copy, props); + return copy; + }); + + /** + * Get currently written HEAD. + */ + this.getvHEAD_1 = () => co(function*() { + if (!vHEAD) { + yield refreshHead(); + } + return vHEAD_1; + }); + + /** + * Utility method: gives the personalized difficulty level of a given issuer for next block. + * @param version The version in which is computed the difficulty. + * @param issuer The issuer we want to get the difficulty level. + */ + this.getIssuerPersonalizedDifficulty = (version, issuer) => co(function *() { + const vHEAD = yield that.getvHeadCopy({ version, issuer }); + yield indexer.preparePersonalizedPoW(vHEAD, vHEAD_1, dal.range, conf); + return vHEAD.issuerDiff; + }); + this.setConfDAL = (newConf, newDAL) => { dal = newDAL; conf = newConf; @@ -30,11 +107,106 @@ function BlockchainContext() { this.checkBlock = (block, withPoWAndSignature) => co(function*(){ if (withPoWAndSignature) { yield Q.nbind(rules.CHECK.ASYNC.ALL_LOCAL, rules, block, conf); - yield Q.nbind(rules.CHECK.ASYNC.ALL_GLOBAL, rules, block, conf, dal); + const index = indexer.localIndex(block, conf); + const mindex = indexer.mindex(index); + const iindex = indexer.iindex(index); + const sindex = indexer.sindex(index); + const cindex = indexer.cindex(index); + const HEAD = yield indexer.completeGlobalScope(block, conf, index, dal); + const HEAD_1 = yield dal.bindexDAL.head(1); + // BR_G49 + if (indexer.ruleVersion(HEAD, HEAD_1) === false) throw Error('ruleVersion'); + // BR_G50 + if (indexer.ruleBlockSize(HEAD) === false) throw Error('ruleBlockSize'); + // BR_G98 + if (indexer.ruleCurrency(block, HEAD) === false) throw Error('ruleCurrency'); + // BR_G51 + if (indexer.ruleNumber(block, HEAD) === false) throw Error('ruleNumber'); + // BR_G52 + if (indexer.rulePreviousHash(block, HEAD) === false) throw Error('rulePreviousHash'); + // BR_G53 + if (indexer.rulePreviousIssuer(block, HEAD) === false) throw Error('rulePreviousIssuer'); + // BR_G101 + if (indexer.ruleIssuerIsMember(HEAD) === false) throw Error('ruleIssuerIsMember'); + // BR_G54 + if (indexer.ruleIssuersCount(block, HEAD) === false) throw Error('ruleIssuersCount'); + // BR_G55 + if (indexer.ruleIssuersFrame(block, HEAD) === false) throw Error('ruleIssuersFrame'); + // BR_G56 + if (indexer.ruleIssuersFrameVar(block, HEAD) === false) throw Error('ruleIssuersFrameVar'); + // BR_G57 + if (indexer.ruleMedianTime(block, HEAD) === false) throw Error('ruleMedianTime'); + // BR_G58 + if (indexer.ruleDividend(block, HEAD) === false) throw Error('ruleDividend'); + // BR_G59 + if (indexer.ruleUnitBase(block, HEAD) === false) throw Error('ruleUnitBase'); + // BR_G60 + if (indexer.ruleMembersCount(block, HEAD) === false) throw Error('ruleMembersCount'); + // BR_G61 + if (indexer.rulePowMin(block, HEAD) === false) throw Error('rulePowMin'); + // BR_G62 + if (indexer.ruleProofOfWork(HEAD) === false) throw Error('ruleProofOfWork'); + // BR_G63 + if (indexer.ruleIdentityWritability(iindex, conf) === false) throw Error('ruleIdentityWritability'); + // BR_G64 + if (indexer.ruleMembershipWritability(mindex, conf) === false) throw Error('ruleMembershipWritability'); + // BR_G65 + if (indexer.ruleCertificationWritability(cindex, conf) === false) throw Error('ruleCertificationWritability'); + // BR_G66 + if (indexer.ruleCertificationStock(cindex, conf) === false) throw Error('ruleCertificationStock'); + // BR_G67 + if (indexer.ruleCertificationPeriod(cindex) === false) throw Error('ruleCertificationPeriod'); + // BR_G68 + if (indexer.ruleCertificationFromMember(HEAD, cindex) === false) throw Error('ruleCertificationFromMember'); + // BR_G69 + if (indexer.ruleCertificationToMemberOrNewcomer(cindex) === false) throw Error('ruleCertificationToMemberOrNewcomer'); + // BR_G70 + if (indexer.ruleCertificationToLeaver(cindex) === false) throw Error('ruleCertificationToLeaver'); + // BR_G71 + if (indexer.ruleCertificationReplay(cindex) === false) throw Error('ruleCertificationReplay'); + // BR_G72 + if (indexer.ruleCertificationSignature(cindex) === false) throw Error('ruleCertificationSignature'); + // BR_G73 + if (indexer.ruleIdentityUIDUnicity(iindex) === false) throw Error('ruleIdentityUIDUnicity'); + // BR_G74 + if (indexer.ruleIdentityPubkeyUnicity(iindex) === false) throw Error('ruleIdentityPubkeyUnicity'); + // BR_G75 + if (indexer.ruleMembershipSuccession(mindex) === false) throw Error('ruleMembershipSuccession'); + // BR_G76 + if (indexer.ruleMembershipDistance(mindex) === false) throw Error('ruleMembershipDistance'); + // BR_G77 + if (indexer.ruleMembershipOnRevoked(mindex) === false) throw Error('ruleMembershipOnRevoked'); + // BR_G78 + if (indexer.ruleMembershipJoinsTwice(mindex) === false) throw Error('ruleMembershipJoinsTwice'); + // BR_G79 + if (indexer.ruleMembershipEnoughCerts(mindex) === false) throw Error('ruleMembershipEnoughCerts'); + // BR_G80 + if (indexer.ruleMembershipLeaverIsMember(mindex) === false) throw Error('ruleMembershipLeaverIsMember'); + // BR_G81 + if (indexer.ruleMembershipActiveIsMember(mindex) === false) throw Error('ruleMembershipActiveIsMember'); + // BR_G82 + if (indexer.ruleMembershipRevokedIsMember(mindex) === false) throw Error('ruleMembershipRevokedIsMember'); + // BR_G83 + if (indexer.ruleMembershipRevokedSingleton(mindex) === false) throw Error('ruleMembershipRevokedSingleton'); + // BR_G84 + if (indexer.ruleMembershipRevocationSignature(mindex) === false) throw Error('ruleMembershipRevocationSignature'); + // BR_G85 + if (indexer.ruleMembershipExcludedIsMember(iindex) === false) throw Error('ruleMembershipExcludedIsMember'); + // BR_G86 + if (indexer.ruleToBeKickedArePresent(mindex, dal) === false) throw Error('ruleToBeKickedArePresent'); + // BR_G103 + if (indexer.ruleTxWritability(sindex) === false) throw Error('ruleToBeKickedArePresent'); + // BR_G87 + if (indexer.ruleInputIsAvailable(sindex) === false) throw Error('ruleInputIsAvailable'); + // BR_G88 + if (indexer.ruleInputIsUnlocked(sindex) === false) throw Error('ruleInputIsUnlocked'); + // BR_G89 + if (indexer.ruleInputIsTimeUnlocked(sindex) === false) throw Error('ruleInputIsTimeUnlocked'); + // BR_G90 + if (indexer.ruleOutputBase(sindex, HEAD_1) === false) throw Error('ruleOutputBase'); } else { yield Q.nbind(rules.CHECK.ASYNC.ALL_LOCAL_BUT_POW, rules, block, conf); - yield Q.nbind(rules.CHECK.ASYNC.ALL_GLOBAL_BUT_POW, rules, block, conf, dal); } // Check document's coherence yield checkIssuer(block); @@ -48,6 +220,7 @@ function BlockchainContext() { block.fork = false; yield saveBlockData(currentBlock, block); logger.info('Block #' + block.number + ' added to the blockchain in %s ms', (new Date() - start)); + vHEAD_1 = vHEAD = HEADrefreshed = null; return block; } catch(err) { @@ -75,6 +248,8 @@ function BlockchainContext() { logger.debug('Reverting block #%s...', current.number); const res = yield that.revertBlock(current); logger.debug('Reverted block #%s', current.number); + // Invalidates the head, since it has changed. + yield refreshHead(); return res; }); @@ -93,18 +268,40 @@ function BlockchainContext() { }); this.revertBlock = (block) => co(function *() { + + const blockstamp = [block.number, block.hash].join('-'); + + // Revert links + const writtenOn = yield dal.cindexDAL.getWrittenOn(blockstamp); + for (const entry of writtenOn) { + const from = yield dal.getWrittenIdtyByPubkey(entry.issuer); + const to = yield dal.getWrittenIdtyByPubkey(entry.receiver); + if (entry.op == constants.IDX_CREATE) { + // We remove the created link + dal.wotb.removeLink(from.wotb_id, to.wotb_id, true); + } else { + // We add the removed link + dal.wotb.addLink(from.wotb_id, to.wotb_id, true); + } + } + + // Revert nodes + yield undoMembersUpdate(block); + + yield dal.bindexDAL.removeBlock(block.number); + yield dal.mindexDAL.removeBlock(blockstamp); + yield dal.iindexDAL.removeBlock(blockstamp); + yield dal.cindexDAL.removeBlock(blockstamp); + yield dal.sindexDAL.removeBlock(blockstamp); + + // Then: normal updates const previousBlock = yield dal.getBlockByNumberAndHashOrNull(block.number - 1, block.previousHash || ''); // Set the block as SIDE block (equivalent to removal from main branch) yield dal.blockDAL.setSideBlock(block, previousBlock); - yield undoCertifications(block); - yield undoLinks(block); - yield dal.unflagExpiredIdentitiesOf(block.number); - yield dal.unflagExpiredCertificationsOf(block.number); - if (previousBlock) { - yield dal.undoObsoleteLinks(previousBlock.medianTime - conf.sigValidity); - } - yield undoMembersUpdate(block); - yield undoTransactionSources(block); + + // Remove any source created for this block (both Dividend and Transaction). + yield dal.removeAllSourcesOfBlock(blockstamp); + yield undoDeleteTransactions(block); }); @@ -134,30 +331,45 @@ function BlockchainContext() { this.current = () => dal.getCurrentBlockOrNull(); const saveBlockData = (current, block) => co(function*() { + + if (block.number == 0) { + yield that.saveParametersForRootBlock(block); + } + + const indexes = yield dal.generateIndexes(block, conf); + + // Newcomers + yield that.createNewcomers(indexes.iindex); + + // Save indexes + yield dal.bindexDAL.saveEntity(indexes.HEAD); + yield dal.mindexDAL.insertBatch(indexes.mindex); + yield dal.iindexDAL.insertBatch(indexes.iindex); + yield dal.sindexDAL.insertBatch(indexes.sindex); + yield dal.cindexDAL.insertBatch(indexes.cindex); + + // Create/Update nodes in wotb + yield that.updateMembers(block); + + yield dal.trimIndexes(block, conf); yield updateBlocksComputedVars(current, block); // Saves the block (DAL) yield dal.saveBlock(block); - yield that.saveParametersForRootBlock(block); - // Create/Update members (create new identities if do not exist) - yield that.updateMembers(block); + + // --> Update links + yield dal.updateWotbLinks(indexes.cindex); + // Create/Update certifications - yield that.updateCertifications(block); - // Save links - yield that.updateLinksForBlocks([block], dal.getBlock.bind(dal)); - // Compute obsolete links - yield that.computeObsoleteLinks(block); - // Compute obsolete memberships (active, joiner) - yield that.computeObsoleteMemberships(block); - // Compute obsolete identities - yield that.computeExpiredIdentities(block); - // Compute obsolete certifications - yield that.computeExpiredCertifications(block); - // Compute obsolete memberships - yield that.computeExpiredMemberships(block); - // Update consumed sources & create new ones - yield that.updateSources(block); + yield that.removeCertificationsFromSandbox(block); + // Create/Update memberships + yield that.removeMembershipsFromSandbox(block); + // Compute to be revoked members + yield that.computeToBeRevoked(indexes.mindex); // Delete eventually present transactions yield that.deleteTransactions(block); + + yield dal.trimSandboxes(block, conf); + return block; }); @@ -171,19 +383,10 @@ function BlockchainContext() { } // UD Time update if (block.number == 0) { - block.UDTime = block.medianTime; // Root = first UD time block.dividend = null; } - else if (block.dividend) { - const result = yield { - last: dal.lastUDBlock(), - root: dal.getBlock(0) - }; - block.UDTime = conf.dt + (result.last ? result.last.UDTime : result.root.medianTime); - } - else { + else if (!block.dividend) { block.dividend = null; - block.UDTime = current.UDTime; } }); @@ -192,31 +395,28 @@ function BlockchainContext() { yield dal.removeUnWrittenWithUID(idty.uid); }); + this.createNewcomers = (iindex) => co(function*() { + for (const entry of iindex) { + if (entry.op == constants.IDX_CREATE) { + // Reserves a wotb ID + entry.wotb_id = dal.wotb.addNode(); + logger.trace('%s was affected wotb_id %s', entry.uid, entry.wotb_id); + // Remove from the sandbox any other identity with the same pubkey/uid, since it has now been reserved. + yield cleanRejectedIdentities({ + pubkey: entry.pub, + uid: entry.uid + }); + } + } + }); + this.updateMembers = (block) => co(function *() { return co(function *() { - // Newcomers - for (const identity of block.identities) { - let idty = Identity.statics.fromInline(identity); - // Computes the hash if not done yet - if (!idty.hash) - idty.hash = (hashf(rawer.getOfficialIdentity(idty)) + "").toUpperCase(); - yield dal.newIdentity(idty); - yield cleanRejectedIdentities(idty); - } // Joiners (come back) for (const inlineMS of block.joiners) { let ms = Membership.statics.fromInline(inlineMS); - yield dal.joinIdentity(ms.issuer, ms.number); - } - // Actives - for (const inlineMS of block.actives) { - let ms = Membership.statics.fromInline(inlineMS); - yield dal.activeIdentity(ms.issuer, ms.number); - } - // Leavers - for (const inlineMS of block.leavers) { - let ms = Membership.statics.fromInline(inlineMS); - yield dal.leaveIdentity(ms.issuer, ms.number); + const idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); + dal.wotb.setEnabled(true, idty.wotb_id); } // Revoked for (const inlineRevocation of block.revoked) { @@ -225,98 +425,29 @@ function BlockchainContext() { } // Excluded for (const excluded of block.excluded) { - dal.excludeIdentity(excluded); + const idty = yield dal.getWrittenIdtyByPubkey(excluded); + dal.wotb.setEnabled(false, idty.wotb_id); } }); }); function undoMembersUpdate (block) { return co(function *() { - yield dal.unFlagToBeKicked(); // Undo 'join' which can be either newcomers or comebackers for (const msRaw of block.joiners) { let ms = Membership.statics.fromInline(msRaw, 'IN', conf.currency); - yield dal.unJoinIdentity(ms); + const idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); + dal.wotb.setEnabled(false, idty.wotb_id); } - // Undo newcomers (may strengthen the undo 'join') + // Undo newcomers for (const identity of block.identities) { - let idty = Identity.statics.fromInline(identity); - yield dal.unacceptIdentity(idty.pubkey); - } - // Undo renew (only remove last membership IN document) - for (const msRaw of block.actives) { - let ms = Membership.statics.fromInline(msRaw, 'IN', conf.currency); - yield dal.unRenewIdentity(ms); + // Does not matter which one it really was, we pop the last X identities + dal.wotb.removeNode(); } - // Undo leavers (forget about their last membership OUT document) - for (const msRaw of block.leavers) { - let ms = Membership.statics.fromInline(msRaw, 'OUT', conf.currency); - yield dal.unLeaveIdentity(ms); - } - // Undo revoked (make them non-revoked) - let revokedPubkeys = []; - for (const inlineRevocation of block.revoked) { - let revocation = Identity.statics.revocationFromInline(inlineRevocation); - revokedPubkeys.push(revocation.pubkey); - yield dal.unrevokeIdentity(revocation.pubkey); - } - // Undo excluded (make them become members again, but set them as 'to be kicked') + // Undo excluded (make them become members again in wotb) for (const pubkey of block.excluded) { - yield dal.unExcludeIdentity(pubkey, revokedPubkeys.indexOf(pubkey) !== -1); - } - }); - } - - function undoCertifications(block) { - return co(function *() { - for (const inlineCert of block.certifications) { - let cert = Certification.statics.fromInline(inlineCert); - let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to); - cert.target = new Identity(toIdty).getTargetHash(); - let existing = yield dal.existsCert(cert); - existing.written_block = null; - existing.written_hash = null; - existing.linked = false; - existing.written = false; - yield dal.saveCert(new Certification(cert)); - } - }); - } - - function undoLinks(block) { - return co(function *() { - for (const inlineCert of block.certifications) { - let cert = Certification.statics.fromInline(inlineCert); - let fromIdty = yield dal.getWrittenIdtyByPubkey(cert.from); - let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to); - dal.removeLink( - new Link({ - source: cert.from, - target: cert.to, - from_wotb_id: fromIdty.wotb_id, - to_wotb_id: toIdty.wotb_id, - timestamp: block.medianTime, - block_number: block.number, - block_hash: block.hash, - obsolete: false - })); - } - }); - } - - function undoTransactionSources(block) { - return co(function *() { - // Remove any source created for this block (both Dividend and Transaction) - dal.removeAllSourcesOfBlock(block.number); - for (const obj of block.transactions) { - obj.version = block.version; - obj.currency = block.currency; - obj.issuers = obj.signatories; - let tx = new Transaction(obj); - let txObj = tx.getTransaction(); - for (const input of txObj.inputs) { - yield dal.unConsumeSource(input.identifier, input.noffset); - } + const idty = yield dal.getWrittenIdtyByPubkey(pubkey); + dal.wotb.setEnabled(true, idty.wotb_id); } }); } @@ -324,7 +455,6 @@ function BlockchainContext() { function undoDeleteTransactions(block) { return co(function *() { for (const obj of block.transactions) { - obj.version = block.version; obj.currency = block.currency; obj.issuers = obj.signatories; let tx = new Transaction(obj); @@ -334,57 +464,53 @@ function BlockchainContext() { } /** - * Historical method that takes certifications from a block and tries to either: - * * Update the certification found in the DB an set it as written - * * Create it if it does not exist + * Delete certifications from the sandbox since it has been written. * - * Has a sibling method named 'updateCertificationsForBlocks'. - * @param block - * @param done + * @param block Block in which are contained the certifications to remove from sandbox. */ - this.updateCertifications = (block) => co(function*() { + this.removeCertificationsFromSandbox = (block) => co(function*() { for (let inlineCert of block.certifications) { let cert = Certification.statics.fromInline(inlineCert); let idty = yield dal.getWritten(cert.to); cert.target = new Identity(idty).getTargetHash(); - const to_uid = idty.uid; - idty = yield dal.getWritten(cert.from); - const from_uid = idty.uid; - const existing = yield dal.existsCert(cert); - if (existing) { - cert = existing; - } - cert.written_block = block.number; - cert.written_hash = block.hash; - cert.from_uid = from_uid; - cert.to_uid = to_uid; - cert.linked = true; - yield dal.officializeCertification(new Certification(cert)); + yield dal.deleteCert(cert); + } + }); + + /** + * Delete memberships from the sandbox since it has been written. + * + * @param block Block in which are contained the certifications to remove from sandbox. + */ + this.removeMembershipsFromSandbox = (block) => co(function*() { + const mss = block.joiners.concat(block.actives).concat(block.leavers); + for (const inlineMS of mss) { + let ms = Membership.statics.fromInline(inlineMS); + yield dal.deleteMS(ms); } }); that.saveParametersForRootBlock = (block) => co(function*() { if (block.parameters) { - const sp = block.parameters.split(':'); - - conf.c = parseFloat(sp[0]); - conf.dt = parseInt(sp[1]); - conf.ud0 = parseInt(sp[2]); - conf.sigPeriod = parseInt(sp[3]); - conf.sigStock = parseInt(sp[4]); - conf.sigWindow = parseInt(sp[5]); - conf.sigValidity = parseInt(sp[6]); - conf.sigQty = parseInt(sp[7]); - conf.idtyWindow = parseInt(sp[8]); - conf.msWindow = parseInt(sp[9]); - conf.xpercent = parseFloat(sp[10]); - conf.msValidity = parseInt(sp[11]); - conf.stepMax = parseInt(sp[12]); - conf.medianTimeBlocks = parseInt(sp[13]); - conf.avgGenTime = parseInt(sp[14]); - conf.dtDiffEval = parseInt(sp[15]); - conf.blocksRot = parseInt(sp[16]); - conf.percentRot = parseFloat(sp[17]); + const bconf = Block.statics.getConf(block); + conf.c = bconf.c; + conf.dt = bconf.dt; + conf.ud0 = bconf.ud0; + conf.sigPeriod = bconf.sigPeriod; + conf.sigStock = bconf.sigStock; + conf.sigWindow = bconf.sigWindow; + conf.sigValidity = bconf.sigValidity; + conf.sigQty = bconf.sigQty; + conf.idtyWindow = bconf.idtyWindow; + conf.msWindow = bconf.msWindow; + conf.xpercent = bconf.xpercent; + conf.msValidity = bconf.msValidity; + conf.stepMax = bconf.stepMax; + conf.medianTimeBlocks = bconf.medianTimeBlocks; + conf.avgGenTime = bconf.avgGenTime; + conf.dtDiffEval = bconf.dtDiffEval; + conf.blocksRot = bconf.blocksRot; + conf.percentRot = bconf.percentRot; conf.currency = block.currency; // Super important: adapt wotb module to handle the correct stock dal.wotb.setMaxCert(conf.sigStock); @@ -392,11 +518,10 @@ function BlockchainContext() { } }); - this.computeObsoleteLinks = (block) => co(function*() { - yield dal.obsoletesLinks(block.medianTime - conf.sigValidity); - const members = yield dal.getMembersWithoutEnoughValidLinks(conf.sigQty); - for (const idty of members) { - yield dal.setKicked(idty.pubkey, new Identity(idty).getTargetHash(), true); + this.computeToBeRevoked = (mindex) => co(function*() { + const revocations = _.filter(mindex, (entry) => entry.revoked_on); + for (const revoked of revocations) { + yield dal.setRevoked(revoked.pub, true); } }); @@ -409,155 +534,11 @@ function BlockchainContext() { throw 'Key ' + target + ' does not have enough links (' + count + '/' + conf.sigQty + ')'; }); - this.computeObsoleteMemberships = (block) => co(function *() { - let lastForKick = yield dal.getMembershipExcludingBlock(block, conf.msValidity); - let lastForRevoke = yield dal.getMembershipRevocatingBlock(block, conf.msValidity * constants.REVOCATION_FACTOR); - if (lastForKick) { - yield dal.kickWithOutdatedMemberships(lastForKick.number); - } - if (lastForRevoke) { - yield dal.revokeWithOutdatedMemberships(lastForRevoke.number); - } - }); - - this.computeExpiredIdentities = (block) => co(function *() { - let lastForExpiry = yield dal.getIdentityExpiringBlock(block, conf.idtyWindow); - if (lastForExpiry) { - yield dal.flagExpiredIdentities(lastForExpiry.number, block.number); - } - }); - - this.computeExpiredCertifications = (block) => co(function *() { - let lastForExpiry = yield dal.getCertificationExpiringBlock(block, conf.certWindow); - if (lastForExpiry) { - yield dal.flagExpiredCertifications(lastForExpiry.number, block.number); - } - }); - - this.computeExpiredMemberships = (block) => co(function *() { - let lastForExpiry = yield dal.getMembershipExpiringBlock(block, conf.certWindow); - if (lastForExpiry) { - yield dal.flagExpiredMemberships(lastForExpiry.number, block.number); - } - }); - - this.updateSources = (block) => co(function*() { - if (block.dividend) { - const idties = yield dal.getMembers(); - for (const idty of idties) { - yield dal.saveSource(new Source({ - 'type': 'D', - 'number': block.number, - 'time': block.medianTime, - 'identifier': idty.pubkey, - 'noffset': block.number, - 'block_hash': block.hash, - 'amount': block.dividend, - 'base': block.unitbase, - 'conditions': 'SIG(' + idty.pubkey + ')', // Only this pubkey can unlock its UD - 'consumed': 0 - })); - } - } - - for (const obj of block.transactions) { - obj.currency = block.currency; - obj.issuers = obj.signatories; - const tx = new Transaction(obj); - const txObj = tx.getTransaction(); - const txHash = tx.getHash(true); - for (const input of txObj.inputs) { - yield dal.setConsumedSource(input.identifier, input.noffset); - } - - let index = 0; - for (const output of txObj.outputs) { - yield dal.saveSource(new Source({ - 'type': 'T', - 'number': block.number, - 'time': block.medianTime, - 'identifier': txHash, - 'noffset': index++, - 'block_hash': block.hash, - 'amount': output.amount, - 'base': output.base, - 'conditions': output.conditions, - 'consumed': 0 - })); - } - } - }); - - /** - * New method for CREATING memberships found in blocks. - * Made for performance reasons, this method will batch insert all memberships at once. - * @param blocks - * @returns {*} - */ - this.updateMembershipsForBlocks = (blocks) => co(function *() { - const memberships = []; - const types = { - 'join': 'joiners', - 'active': 'actives', - 'leave': 'leavers' - }; - for (const block of blocks) { - _.keys(types).forEach(function(type){ - const msType = type == 'leave' ? 'out' : 'in'; - const field = types[type]; - const mss = block[field]; - for (const msRaw of mss) { - const ms = Membership.statics.fromInline(msRaw, type == 'leave' ? 'OUT' : 'IN', block.currency); - ms.membership = msType.toUpperCase(); - ms.written = true; - ms.written_number = block.number; - ms.type = type; - ms.hash = String(hashf(ms.getRawSigned())).toUpperCase(); - ms.idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase(); - memberships.push(ms); - } - }); - } - return dal.updateMemberships(memberships); - }); - - /** - * New method for CREATING links found in blocks. - * Made for performance reasons, this method will batch insert all links at once. - * @param blocks - * @param getBlock - * @returns {*} - */ - this.updateLinksForBlocks = (blocks, getBlock) => co(function *() { - let links = []; - for (const block of blocks) { - for (const inlineCert of block.certifications) { - let cert = Certification.statics.fromInline(inlineCert); - let tagBlock = block; - if (block.number > 0) { - tagBlock = yield getBlock(cert.block_number); - } - let fromIdty = yield dal.getWrittenIdtyByPubkey(cert.from); - let toIdty = yield dal.getWrittenIdtyByPubkey(cert.to); - links.push({ - source: cert.from, - target: cert.to, - from_wotb_id: fromIdty.wotb_id, - to_wotb_id: toIdty.wotb_id, - timestamp: tagBlock.medianTime, - block_number: block.number, - block_hash: block.hash, - obsolete: false - }); - } - } - return dal.updateLinks(links); - }); - /** * New method for CREATING transactions found in blocks. * Made for performance reasons, this method will batch insert all transactions at once. * @param blocks + * @param getBlockByNumberAndHash * @returns {*} */ this.updateTransactionsForBlocks = (blocks, getBlockByNumberAndHash) => co(function *() { @@ -585,80 +566,6 @@ function BlockchainContext() { return dal.updateTransactions(txs); }); - /** - * New method for CREATING certifications found in blocks. - * Made for performance reasons, this method will batch insert all certifications at once. - * @param blocks - * @returns {*} - */ - this.updateCertificationsForBlocks = (blocks) => co(function *() { - const certs = []; - for (const block of blocks) { - for (const inlineCert of block.certifications) { - let cert = Certification.statics.fromInline(inlineCert); - const to = yield dal.getWrittenIdtyByPubkey(cert.to); - const to_uid = to.uid; - cert.target = new Identity(to).getTargetHash(); - const from = yield dal.getWrittenIdtyByPubkey(cert.from); - const from_uid = from.uid; - const existing = yield dal.existsCert(cert); - if (existing) { - cert = existing; - } - cert.written_block = block.number; - cert.written_hash = block.hash; - cert.from_uid = from_uid; - cert.to_uid = to_uid; - cert.linked = true; - certs.push(cert); - } - } - return dal.updateCertifications(certs); - }); - - /** - * New method for CREATING sources found in transactions of blocks. - * Made for performance reasons, this method will batch insert all sources at once. - * @param blocks - * @returns {*} - */ - this.updateTransactionSourcesForBlocks = (blocks, dividends) => co(function *() { - let sources = dividends; - for (const block of blocks) { - // Transactions - for (const json of block.transactions) { - let obj = json; - obj.version = block.version; - obj.currency = block.currency; - obj.issuers = json.signatories; - let tx = new Transaction(obj); - let txObj = tx.getTransaction(); - let txHash = tx.getHash(true); - sources = sources.concat(txObj.inputs.map((input) => _.extend({ toConsume: true }, input))); - sources = sources.concat(txObj.outputs.map((output, index) => _.extend({ - toConsume: false - }, { - 'type': 'T', - 'number': block.number, - 'block_hash': block.hash, - 'fingerprint': txHash, - 'amount': output.amount, - 'base': output.base, - 'consumed': false, - 'identifier': txHash, - 'noffset': index, - 'conditions': output.conditions - }))); - } - } - try { - let res = yield dal.updateSources(sources); - return res; - } catch (e) { - throw e; - } - }); - this.deleteTransactions = (block) => co(function*() { for (const obj of block.transactions) { obj.currency = block.currency; diff --git a/app/lib/computation/permanentProver.js b/app/lib/computation/permanentProver.js index 197522945f122f33b6961d8acaba85b6e3518d2d..fc65d7a96cbddb85722819b4c4a3454023298258 100644 --- a/app/lib/computation/permanentProver.js +++ b/app/lib/computation/permanentProver.js @@ -59,7 +59,7 @@ function PermanentProver(server) { throw 'Waiting for a root block before computing new blocks'; } const version = current ? current.version : constants.BLOCK_GENERATED_VERSION; - const trial = yield rules.HELPERS.getTrialLevel(version, selfPubkey, conf, dal); + const trial = yield server.getBcContext().getIssuerPersonalizedDifficulty(version, selfPubkey); if (trial > (current.powMin + constants.POW_MAXIMUM_ACCEPTABLE_HANDICAP)) { logger.debug('Trial = %s, powMin = %s, pubkey = %s', trial, current.powMin, selfPubkey.slice(0, 6)); throw 'Too high difficulty: waiting for other members to write next block'; @@ -107,7 +107,7 @@ function PermanentProver(server) { co(function*() { try { const block2 = yield server.BlockchainService.generateNext(); - const trial2 = yield rules.HELPERS.getTrialLevel(block2.version, server.keyPair.publicKey, server.conf, server.dal); + const trial2 = yield server.getBcContext().getIssuerPersonalizedDifficulty(block2.version, server.keyPair.publicKey); lastComputedBlock = yield server.BlockchainService.makeNextBlock(block2, trial2); yield onBlockCallback(lastComputedBlock); } catch (e) { diff --git a/app/lib/constants.js b/app/lib/constants.js index 5f3c2971e5c2c7b3afae72cc61be8a671e04e383..62dcd5d8f0a0a3f98a364fb26cd6096e23c0605e 100644 --- a/app/lib/constants.js +++ b/app/lib/constants.js @@ -109,6 +109,7 @@ module.exports = { BLOCK_ALREADY_PROCESSED: { httpCode: 400, uerr: { ucode: 2028, message: 'Already processed' }}, TOO_OLD_MEMBERSHIP: { httpCode: 400, uerr: { ucode: 2029, message: "Too old membership." }}, TX_ALREADY_PROCESSED: { httpCode: 400, uerr: { ucode: 2030, message: "Transaction already processed" }}, + A_MORE_RECENT_MEMBERSHIP_EXISTS: { httpCode: 400, uerr: { ucode: 2031, message: "A more recent membership already exists" }} }, DEBUG: { @@ -374,7 +375,15 @@ module.exports = { TRANSACTION_MAX_TRIES: 10, NONCE_RANGE: 1000 * 1000 * 1000 * 100, - POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64 + POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64, + + // INDEXES + M_INDEX: 'MINDEX', + I_INDEX: 'IINDEX', + S_INDEX: 'SINDEX', + C_INDEX: 'CINDEX', + IDX_CREATE: 'CREATE', + IDX_UPDATE: 'UPDATE' }; function exact (regexpContent) { diff --git a/app/lib/dal/drivers/sqlite.js b/app/lib/dal/drivers/sqlite.js index 4a38c4723d3088e4b81b4db02ff29d0939ad4591..8389827759e55865ca88860f3e76f221514f4324 100644 --- a/app/lib/dal/drivers/sqlite.js +++ b/app/lib/dal/drivers/sqlite.js @@ -31,7 +31,7 @@ function SQLiteDriver(path) { const db = yield getDB(); return new Promise((resolve, reject) => db.all(sql, params, (err, rows) => { if (err) { - return reject(err); + return reject(Error('SQL error "' + err.message + '" on query "' + sql + '"')); } else { return resolve(rows); } @@ -42,7 +42,7 @@ function SQLiteDriver(path) { const db = yield getDB(); return new Promise((resolve, reject) => db.exec(sql, (err) => { if (err) { - return reject(err); + return reject(Error('SQL error "' + err.message + '" on query "' + sql + '"')); } else { return resolve(); } diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js index 78b49c26f8e70c4968b47d7cfb78dee27e587c34..4a2bfaaa53ca0baac98ee3189a636b3527f1f3e7 100644 --- a/app/lib/dal/fileDAL.js +++ b/app/lib/dal/fileDAL.js @@ -2,18 +2,17 @@ const Q = require('q'); const co = require('co'); const _ = require('underscore'); +const indexer = require('../dup/indexer'); const hashf = require('../ucp/hashf'); const wotb = require('../wot'); const logger = require('../logger')('filedal'); const directory = require('../system/directory'); const Configuration = require('../entity/configuration'); -const Membership = require('../entity/membership'); const Merkle = require('../entity/merkle'); const Transaction = require('../entity/transaction'); const constants = require('../constants'); const ConfDAL = require('./fileDALs/confDAL'); const StatDAL = require('./fileDALs/statDAL'); -const IndicatorsDAL = require('./fileDALs/IndicatorsDAL'); const CFSStorage = require('./fileDALs/AbstractCFS'); module.exports = (params) => { @@ -25,25 +24,26 @@ function FileDAL(params) { const rootPath = params.home; const myFS = params.fs; const sqliteDriver = params.dbf(); - const wotbInstance = params.wotb; const that = this; this.profile = 'DAL'; - this.wotb = wotbInstance; + this.wotb = params.wotb; // DALs this.confDAL = new ConfDAL(rootPath, myFS, null, that, CFSStorage); this.metaDAL = new (require('./sqliteDAL/MetaDAL'))(sqliteDriver); this.peerDAL = new (require('./sqliteDAL/PeerDAL'))(sqliteDriver); this.blockDAL = new (require('./sqliteDAL/BlockDAL'))(sqliteDriver); - this.sourcesDAL = new (require('./sqliteDAL/SourcesDAL'))(sqliteDriver); this.txsDAL = new (require('./sqliteDAL/TxsDAL'))(sqliteDriver); - this.indicatorsDAL = new IndicatorsDAL(rootPath, myFS, null, that, CFSStorage); this.statDAL = new StatDAL(rootPath, myFS, null, that, CFSStorage); - this.linksDAL = new (require('./sqliteDAL/LinksDAL'))(sqliteDriver, wotbInstance); - this.idtyDAL = new (require('./sqliteDAL/IdentityDAL'))(sqliteDriver, wotbInstance); + this.idtyDAL = new (require('./sqliteDAL/IdentityDAL'))(sqliteDriver); this.certDAL = new (require('./sqliteDAL/CertDAL'))(sqliteDriver); this.msDAL = new (require('./sqliteDAL/MembershipDAL'))(sqliteDriver); + this.bindexDAL = new (require('./sqliteDAL/index/BIndexDAL'))(sqliteDriver); + this.mindexDAL = new (require('./sqliteDAL/index/MIndexDAL'))(sqliteDriver); + this.iindexDAL = new (require('./sqliteDAL/index/IIndexDAL'))(sqliteDriver); + this.sindexDAL = new (require('./sqliteDAL/index/SIndexDAL'))(sqliteDriver); + this.cindexDAL = new (require('./sqliteDAL/index/CIndexDAL'))(sqliteDriver); this.newDals = { 'metaDAL': that.metaDAL, @@ -51,11 +51,8 @@ function FileDAL(params) { 'certDAL': that.certDAL, 'msDAL': that.msDAL, 'idtyDAL': that.idtyDAL, - 'sourcesDAL': that.sourcesDAL, - 'linksDAL': that.linksDAL, 'txsDAL': that.txsDAL, 'peerDAL': that.peerDAL, - 'indicatorsDAL': that.indicatorsDAL, 'confDAL': that.confDAL, 'statDAL': that.statDAL, 'ghostDAL': { @@ -72,7 +69,12 @@ function FileDAL(params) { 'CREATE VIEW IF NOT EXISTS network AS select i.uid, (last_try - first_down) / 1000 as down_delay_in_sec, p.* from peer p LEFT JOIN idty i on i.pubkey = p.pubkey ORDER by down_delay_in_sec;' + 'COMMIT;'); }) - } + }, + 'bindexDAL': that.bindexDAL, + 'mindexDAL': that.mindexDAL, + 'iindexDAL': that.iindexDAL, + 'sindexDAL': that.sindexDAL, + 'cindexDAL': that.cindexDAL }; let currency = ''; @@ -85,7 +87,8 @@ function FileDAL(params) { } logger.debug("Upgrade database..."); yield that.metaDAL.upgradeDatabase(); - const latestMember = yield that.idtyDAL.getLatestMember(); + // TODO: remove as of v1.0 + const latestMember = yield that.iindexDAL.getLatestMember(); if (latestMember && that.wotb.getWoTSize() > latestMember.wotb_id + 1) { logger.warn('Maintenance: cleaning wotb...'); while (that.wotb.getWoTSize() > latestMember.wotb_id + 1) { @@ -101,8 +104,6 @@ function FileDAL(params) { this.getDBVersion = () => that.metaDAL.getVersion(); - this.getCurrency = () => currency; - that.writeFileOfBlock = (block) => that.blockDAL.saveBlock(block); this.writeSideFileOfBlock = (block) => @@ -110,37 +111,6 @@ function FileDAL(params) { this.listAllPeers = () => that.peerDAL.listAll(); - function nullIfError(promise, done) { - return promise - .then(function (p) { - done && done(null, p); - return p; - }) - .catch(function () { - done && done(null, null); - return null; - }); - } - - function nullIfErrorIs(promise, expectedError, done) { - return promise - .then(function (p) { - done && done(null, p); - return p; - }) - .catch(function (err) { - if (err == expectedError) { - done && done(null, null); - return null; - } - if (done) { - done(err); - return null; - } - throw err; - }); - } - this.getPeer = (pubkey) => co(function*() { try { return that.peerDAL.getPeer(pubkey) @@ -186,28 +156,35 @@ function FileDAL(params) { }); this.getBlockByNumberAndHashOrNull = (number, hash) => co(function*() { - return yield nullIfError(that.getBlockByNumberAndHash(number, hash)); - }); - - this.getChainabilityBlock = (currentTime, sigPeriod) => co(function *() { - // AGE = current_time - block_time - // CHAINABLE = AGE >= sigPeriod - // CHAINABLE = block_time =< current_time - sigPeriod - return that.blockDAL.getMoreRecentBlockWithTimeEqualBelow(currentTime - sigPeriod); + try { + return yield that.getBlockByNumberAndHash(number, hash); + } catch (e) { + return null; + } }); - this.existsNonChainableLink = (from, chainabilityBlockNumber, sigStock) => co(function *() { + this.existsNonChainableLink = (from, vHEAD_1, sigStock) => co(function *() { // Cert period rule - let links = yield that.linksDAL.getLinksOfIssuerAbove(from, chainabilityBlockNumber); - if (links.length > 0) return true; + const medianTime = vHEAD_1 ? vHEAD_1.medianTime : 0; + const linksFrom = yield that.cindexDAL.reducablesFrom(from); + const unchainables = _.filter(linksFrom, (link) => link.chainable_on > medianTime); + if (unchainables.length > 0) return true; // Max stock rule - let activeLinks = yield that.linksDAL.getValidLinksFrom(from); + let activeLinks = _.filter(linksFrom, (link) => !link.expired_on); return activeLinks.length >= sigStock; }); this.getCurrentBlockOrNull = () => co(function*() { - return nullIfErrorIs(that.getBlockCurrent(), constants.ERROR.BLOCK.NO_CURRENT_BLOCK); + let current = null; + try { + current = yield that.getBlockCurrent(); + } catch (e) { + if (e != constants.ERROR.BLOCK.NO_CURRENT_BLOCK) { + throw e; + } + } + return current; }); this.getPromoted = (number) => that.getBlock(number); @@ -223,8 +200,6 @@ function FileDAL(params) { this.getCountOfPoW = (issuer) => that.blockDAL.getCountOfBlocksIssuedBy(issuer); - this.getNbIssuedInFrame = (issuer, from) => that.blockDAL.getNbIssuedFrom(issuer, from); - this.getBlocksBetween = (start, end) => Q(this.blockDAL.getBlocks(Math.max(0, start), end)); this.getForkBlocksFollowing = (current) => this.blockDAL.getNextForkBlocks(current.number, current.hash); @@ -236,66 +211,32 @@ function FileDAL(params) { return current; }); - this.getBlockFrom = (number) => co(function*() { - const current = yield that.getCurrentBlockOrNull(); - return that.getBlocksBetween(number, current.number); - }); - - this.getValidLinksFrom = (from) => that.linksDAL.getValidLinksFrom(from); - - this.getValidLinksTo = (to) => that.linksDAL.getValidLinksTo(to); - - this.getMembersWithoutEnoughValidLinks = (sigQty) => that.idtyDAL.query('' + - 'SELECT * ' + - 'FROM idty i ' + - 'WHERE member ' + - 'AND (' + - ' SELECT count(*) ' + - ' FROM link lnk ' + - ' WHERE NOT lnk.obsolete ' + - ' AND lnk.target = i.pubkey' + - ') < ?', [sigQty]); + this.getValidLinksTo = (to) => that.cindexDAL.getValidLinksTo(to); - this.getPreviousLinks = (from, to) => co(function *() { - let links = yield that.linksDAL.getLinksWithPath(from, to); - links = _.sortBy(links, 'timestamp'); - return links[links.length - 1]; - }); - - this.getValidFromTo = (from, to) => co(function*() { - const links = that.getValidLinksFrom(from); - return _.chain(links).where({target: to}).value(); - }); + this.getAvailableSourcesByPubkey = (pubkey) => this.sindexDAL.getAvailableForPubkey(pubkey); - this.getLastValidFrom = (from) => co(function *() { - let links = yield that.linksDAL.getLinksFrom(from); - links = _.sortBy(links, 'timestamp'); - return links[links.length - 1]; + this.getIdentityByHashOrNull = (hash) => co(function*() { + const pending = yield that.idtyDAL.getByHash(hash); + if (!pending) { + return that.iindexDAL.getFromHash(hash); + } + return pending; }); - this.getAvailableSourcesByPubkey = function (pubkey) { - return that.sourcesDAL.getAvailableForPubkey(pubkey); - }; - - this.getIdentityByHashOrNull = (hash) => that.idtyDAL.getByHash(hash); - - this.getMembers = () => co(function*() { - const idties = yield that.idtyDAL.getWhoIsOrWasMember() - return _.chain(idties).where({member: true}).value(); - }); + this.getMembers = () => that.iindexDAL.getMembers(); // TODO: this should definitely be reduced by removing fillInMembershipsOfIdentity this.getWritten = (pubkey) => co(function*() { try { - return yield that.fillInMembershipsOfIdentity(that.idtyDAL.getFromPubkey(pubkey)); + return yield that.fillInMembershipsOfIdentity(that.iindexDAL.getFromPubkey(pubkey)); } catch (err) { logger.error(err); return null; } }); - this.getWrittenIdtyByPubkey = (pubkey) => this.idtyDAL.getFromPubkey(pubkey); - this.getWrittenIdtyByUID = (pubkey) => this.idtyDAL.getFromUID(pubkey); + this.getWrittenIdtyByPubkey = (pubkey) => this.iindexDAL.getFromPubkey(pubkey); + this.getWrittenIdtyByUID = (pubkey) => this.iindexDAL.getFromUID(pubkey); this.fillInMembershipsOfIdentity = (queryPromise) => co(function*() { try { @@ -327,32 +268,81 @@ function FileDAL(params) { return _.chain(pending).where({pubkey: pubkey}).value(); }); - this.getToBeKicked = () => co(function*() { - const membersOnce = yield that.idtyDAL.getWhoIsOrWasMember(); - return _.chain(membersOnce).where({member: true, kick: true}).value(); - }); - this.getRevocatingMembers = () => co(function *() { - return that.idtyDAL.getToRevoke(); + const revoking = yield that.idtyDAL.getToRevoke(); + const toRevoke = []; + for (const pending of revoking) { + const idty = yield that.getWrittenIdtyByPubkey(pending.pubkey); + if (!idty.revoked_on) { + toRevoke.push(pending); + } + } + return toRevoke; }); - this.getToBeKickedPubkeys = () => co(function *() { - const exclusions = yield that.getToBeKicked(); - return _.pluck(exclusions, 'pubkey'); - }); + this.getToBeKickedPubkeys = () => that.iindexDAL.getToBeKickedPubkeys(); - this.searchJustIdentities = (search) => this.idtyDAL.searchThoseMatching(search); + this.searchJustIdentities = (search) => co(function*() { + const pendings = yield that.idtyDAL.searchThoseMatching(search); + const writtens = yield that.iindexDAL.searchThoseMatching(search); + const nonPendings = _.filter(writtens, (w) => { + return _.where(pendings, { pubkey: w.pub }).length == 0; + }); + const found = pendings.concat(nonPendings); + return yield found.map(f => co(function*() { + const ms = yield that.mindexDAL.getReducedMS(f.pub); + if (ms) { + f.revoked_on = ms.revoked_on ? parseInt(ms.revoked_on) : null; + f.revoked = !!f.revoked_on; + f.revocation_sig = ms.revocation || null; + } + return f; + })) + }); - this.certsToTarget = (hash) => co(function*() { + this.certsToTarget = (pub, hash) => co(function*() { const certs = yield that.certDAL.getToTarget(hash); - const matching = _.chain(certs).sortBy((c) => -c.block).value(); + const links = yield that.cindexDAL.getValidLinksTo(pub); + let matching = certs; + links.map((entry) => { + entry.from = entry.issuer; + const co = entry.created_on.split('-'); + const wo = entry.written_on.split('-'); + entry.block = parseInt(co[0]); + entry.block_number = parseInt(co[0]); + entry.block_hash = co[1]; + entry.linked = true; + entry.written_block = parseInt(wo[0]); + entry.written_hash = wo[1]; + matching.push(entry); + }); + matching = _.sortBy(matching, (c) => -c.block); matching.reverse(); return matching; }); this.certsFrom = (pubkey) => co(function*() { - const certs = yield that.certDAL.getFromPubkey(pubkey); - return _.chain(certs).where({from: pubkey}).sortBy((c) => c.block).value(); + const certs = yield that.certDAL.getFromPubkeyCerts(pubkey); + const links = yield that.cindexDAL.getValidLinksFrom(pubkey); + let matching = certs; + yield links.map((entry) => co(function*() { + const idty = yield that.getWrittenIdtyByPubkey(entry.receiver); + entry.from = entry.issuer; + entry.to = entry.receiver; + const co = entry.created_on.split('-'); + const wo = entry.written_on.split('-'); + entry.block = parseInt(co[0]); + entry.block_number = parseInt(co[0]); + entry.block_hash = co[1]; + entry.target = idty.hash; + entry.linked = true; + entry.written_block = parseInt(wo[0]); + entry.written_hash = wo[1]; + matching.push(entry); + })); + matching = _.sortBy(matching, (c) => -c.block); + matching.reverse(); + return matching; }); this.certsFindNew = () => co(function*() { @@ -365,12 +355,14 @@ function FileDAL(params) { return _.chain(certs).sortBy((c) => -c.block).value(); }); - this.getMembershipForHashAndIssuer = (ms) => co(function*() { - try { - return that.msDAL.getMembershipOfIssuer(ms); - } catch (err) { - return null; + this.getMostRecentMembershipNumberForIssuer = (issuer) => co(function*() { + const mss = yield that.msDAL.getMembershipsOfIssuer(issuer); + const reduced = yield that.mindexDAL.getReducedMS(issuer); + let max = reduced ? parseInt(reduced.created_on) : -1; + for (const ms of mss) { + max = Math.max(ms.number, max); } + return max; }); this.lastJoinOfIdentity = (target) => co(function *() { @@ -388,13 +380,11 @@ function FileDAL(params) { return _.chain(mss).sortBy((ms) => -ms.sigDate).value(); }); - this.existsLinkFromOrAfterDate = (from, to, minDate) => co(function *() { - const links = yield that.linksDAL.getSimilarLinksFromDate(from, to, minDate); - return links.length ? true : false; - }); + this.existsNonReplayableLink = (from, to) => this.cindexDAL.existsNonReplayableLink(from, to); - this.getSource = (identifier, noffset) => co(function*() { - let src = yield that.sourcesDAL.getSource(identifier, noffset); + this.getSource = (identifier, pos) => co(function*() { + // TODO: remove for version 1.0 + let src = yield that.sindexDAL.getSource(identifier, pos); // If the source does not exist, we try to check if it exists under another form (issue #735) if (!src) { let txs = yield that.txsDAL.getTransactionByExtendedHash(identifier); @@ -403,14 +393,14 @@ function FileDAL(params) { } if (txs.length && txs[0].version == 3) { // Other try: V4 - src = yield that.sourcesDAL.getSource(txs[0].v4_hash, noffset); + src = yield that.sindexDAL.getSource(txs[0].v4_hash, pos); if (!src) { // Another try: V5 - src = yield that.sourcesDAL.getSource(txs[0].v5_hash, noffset); + src = yield that.sindexDAL.getSource(txs[0].v5_hash, pos); } if (!src) { // Final try: V3 (because identifier maybe be one of [hash, v4_hash, v5_hash] - src = yield that.sourcesDAL.getSource(txs[0].hash, noffset); + src = yield that.sindexDAL.getSource(txs[0].hash, pos); } } } @@ -419,185 +409,64 @@ function FileDAL(params) { this.isMember = (pubkey) => co(function*() { try { - const idty = yield that.idtyDAL.getFromPubkey(pubkey); + const idty = yield that.iindexDAL.getFromPubkey(pubkey); return idty.member; } catch (err) { return false; } }); - this.isLeaving = (pubkey) => co(function *() { - let idty = yield that.idtyDAL.getFromPubkey(pubkey); - return idty && idty.leaving || false; - }); - this.isMemberAndNonLeaver = (pubkey) => co(function*() { try { - const idty = yield that.idtyDAL.getFromPubkey(pubkey); - return (idty && idty.member && !idty.leaving || false); + const idty = yield that.iindexDAL.getFromPubkey(pubkey); + if (idty && idty.member) { + return !(yield that.isLeaving(pubkey)); + } + return false; } catch (err) { return false; } }); - this.existsCert = (cert) => that.certDAL.existsGivenCert(cert); + this.isLeaving = (pubkey) => co(function*() { + const ms = yield that.mindexDAL.getReducedMS(pubkey); + return (ms && ms.leaving) || false; + }); - this.obsoletesLinks = (minTimestamp) => that.linksDAL.obsoletesLinks(minTimestamp); + this.existsCert = (cert) => co(function*() { + const existing = yield that.certDAL.existsGivenCert(cert); + if (existing) return existing; + const existsLink = yield that.cindexDAL.existsNonReplayableLink(cert.from, cert.to); + return !!existsLink; + }); - this.undoObsoleteLinks = (minTimestamp) => that.linksDAL.unObsoletesLinks(minTimestamp); + this.deleteCert = (cert) => that.certDAL.deleteCert(cert); - this.setConsumedSource = (identifier, noffset) => that.sourcesDAL.consumeSource(identifier, noffset); + this.deleteMS = (ms) => that.msDAL.deleteMS(ms); - this.setKicked = (pubkey, hash, notEnoughLinks) => co(function*() { - const kick = notEnoughLinks ? true : false; - const idty = yield that.idtyDAL.getFromPubkey(pubkey); - if (idty.kick != kick) { - idty.kick = kick; - return yield that.idtyDAL.saveIdentity(idty); - } + this.setRevoked = (pubkey) => co(function*() { + const idty = yield that.getWrittenIdtyByPubkey(pubkey); + idty.revoked = true; + return yield that.idtyDAL.saveIdentity(idty); }); - this.setRevocating = (hash, revocation_sig) => co(function *() { - let idty = yield that.idtyDAL.getByHash(hash); - idty.revocation_sig = revocation_sig; - return that.idtyDAL.saveIdentity(idty); - }); - - this.getMembershipExcludingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring( - current, - msValidtyTime, - that.indicatorsDAL.getCurrentMembershipExcludingBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentExcluding.bind(that.indicatorsDAL) - ); - - this.getMembershipRevocatingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring( - current, - msValidtyTime, - that.indicatorsDAL.getCurrentMembershipRevocatingBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentRevocating.bind(that.indicatorsDAL) - ); - - this.getCertificationExcludingBlock = (current, certValidtyTime) => getCurrentExcludingOrExpiring( - current, - certValidtyTime, - that.indicatorsDAL.getCurrentCertificationExcludingBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentExcludingForCert.bind(that.indicatorsDAL) - ); - - this.getIdentityExpiringBlock = (current, idtyValidtyTime) => getCurrentExcludingOrExpiring( - current, - idtyValidtyTime, - that.indicatorsDAL.getCurrentIdentityExpiringBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentExpiringForIdty.bind(that.indicatorsDAL) - ); - - this.getCertificationExpiringBlock = (current, certWindow) => getCurrentExcludingOrExpiring( - current, - certWindow, - that.indicatorsDAL.getCurrentCertificationExpiringBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentExpiringForCert.bind(that.indicatorsDAL) - ); - - this.getMembershipExpiringBlock = (current, msWindow) => getCurrentExcludingOrExpiring( - current, - msWindow, - that.indicatorsDAL.getCurrentMembershipExpiringBlock.bind(that.indicatorsDAL), - that.indicatorsDAL.writeCurrentExpiringForMembership.bind(that.indicatorsDAL) - ); - - this.nextBlockWithDifferentMedianTime = (block) => that.blockDAL.nextBlockWithDifferentMedianTime(block); - - function getCurrentExcludingOrExpiring(current, delayMax, currentGetter, currentSetter) { - return co(function *() { - let currentExcluding; - if (current.number > 0) { - try { - currentExcluding = yield currentGetter(); - } catch (e) { - currentExcluding = null; - } - } - if (!currentExcluding) { - const root = yield that.getRootBlock(); - const delaySinceStart = current.medianTime - root.medianTime; - if (delaySinceStart > delayMax) { - currentExcluding = root; - } - } - if (currentExcluding) { - // Check current position - const nextBlock = yield that.nextBlockWithDifferentMedianTime(currentExcluding); - if (isExcluding(current, currentExcluding, nextBlock, delayMax)) { - return currentExcluding; - } else { - // Have to look for new one - const start = currentExcluding.number; - let newExcluding; - let top = current.number; - let bottom = start; - // Binary tree search - do { - let middle = top - bottom; - if (middle % 2 != 0) { - middle = middle + 1; - } - middle /= 2; - middle += bottom; - if (middle == top) { - middle--; - bottom--; // Helps not being stuck looking at 'top' - } - const middleBlock = yield that.getBlock(middle); - const middleNextB = yield that.getBlock(middle + 1); - const delaySinceMiddle = current.medianTime - middleBlock.medianTime; - const delaySinceNextB = current.medianTime - middleNextB.medianTime; - const isValidPeriod = delaySinceMiddle <= delayMax; - const isValidPeriodB = delaySinceNextB <= delayMax; - const isExcludin = !isValidPeriod && isValidPeriodB; - //console.log('CRT: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP'); - if (isExcludin) { - // Found - yield currentSetter(middleBlock); - newExcluding = middleBlock; - } - else if (isValidPeriod) { - // Look down in the blockchain - top = middle; - } - else { - // Look up in the blockchain - bottom = middle; - } - } while (!newExcluding); - return newExcluding; - } - } - }); - } - - /** - * Checks if `excluding` is still an excluding block, and its follower `nextBlock` is not, in reference to `current`. - * @param current HEAD of the blockchain. - * @param excluding The block we test if it is still excluding. - * @param nextBlock The block that might be the new excluding block. - * @param maxWindow The time window for exclusion. - * @returns {boolean} - */ - const isExcluding = (current, excluding, nextBlock, maxWindow) => { - const delayFromExcludingToHead = current.medianTime - excluding.medianTime; - const delayFromNextToHead = current.medianTime - nextBlock.medianTime; - const isValidPeriod = delayFromExcludingToHead <= maxWindow; - const isValidPeriodB = delayFromNextToHead <= maxWindow; - return !isValidPeriod && isValidPeriodB; - }; - - this.flagExpiredIdentities = (maxNumber, onNumber) => this.idtyDAL.flagExpiredIdentities(maxNumber, onNumber); - this.flagExpiredCertifications = (maxNumber, onNumber) => this.certDAL.flagExpiredCertifications(maxNumber, onNumber); - this.flagExpiredMemberships = (maxNumber, onNumber) => this.msDAL.flagExpiredMemberships(maxNumber, onNumber); - this.kickWithOutdatedMemberships = (maxNumber) => this.idtyDAL.kickMembersForMembershipBelow(maxNumber); - this.revokeWithOutdatedMemberships = (maxNumber) => this.idtyDAL.revokeMembersForMembershipBelow(maxNumber); + this.setRevocating = (existing, revocation_sig) => co(function *() { + existing.revocation_sig = revocation_sig; + existing.revoked = false; + return that.idtyDAL.saveIdentity(existing); + }); - this.getPeerOrNull = (pubkey) => nullIfError(that.getPeer(pubkey)); + this.getPeerOrNull = (pubkey) => co(function*() { + let peer = null; + try { + peer = yield that.getPeer(pubkey); + } catch (e) { + if (e != constants.ERROR.BLOCK.NO_CURRENT_BLOCK) { + throw e; + } + } + return peer; + }); this.findAllPeersNEWUPBut = (pubkeys) => co(function*() { const peers = yield that.listAllPeers(); @@ -667,23 +536,53 @@ function FileDAL(params) { block.wrong = false; yield [ that.saveBlockInFile(block, true), - that.saveTxsInFiles(block.transactions, {block_number: block.number, time: block.medianTime }), - that.saveMemberships('join', block.joiners, block.number), - that.saveMemberships('active', block.actives, block.number), - that.saveMemberships('leave', block.leavers, block.number) + that.saveTxsInFiles(block.transactions, {block_number: block.number, time: block.medianTime, currency: block.currency }) ]; }); - this.saveMemberships = (type, mss, blockNumber) => { - const msType = type == 'leave' ? 'out' : 'in'; - return mss.reduce((p, msRaw) => p.then(() => { - const ms = Membership.statics.fromInline(msRaw, type == 'leave' ? 'OUT' : 'IN', that.getCurrency()); - ms.type = type; - ms.hash = String(hashf(ms.getRawSigned())).toUpperCase(); - ms.idtyHash = (hashf(ms.userid + ms.certts + ms.issuer) + "").toUpperCase(); - return that.msDAL.saveOfficialMS(msType, ms, blockNumber); - }), Q()); - }; + this.generateIndexes = (block, conf) => co(function*() { + const index = indexer.localIndex(block, conf); + let mindex = indexer.mindex(index); + let iindex = indexer.iindex(index); + let sindex = indexer.sindex(index); + let cindex = indexer.cindex(index); + const HEAD = yield indexer.completeGlobalScope(block, conf, index, that); + sindex = sindex.concat(yield indexer.ruleIndexGenDividend(HEAD, that)); + cindex = cindex.concat(yield indexer.ruleIndexGenCertificationExpiry(HEAD, that)); + mindex = mindex.concat(yield indexer.ruleIndexGenMembershipExpiry(HEAD, that)); + iindex = iindex.concat(yield indexer.ruleIndexGenExclusionByMembership(HEAD, mindex)); + iindex = iindex.concat(yield indexer.ruleIndexGenExclusionByCertificatons(HEAD, cindex, conf, that)); + mindex = mindex.concat(yield indexer.ruleIndexGenImplicitRevocation(HEAD, that)); + yield indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, that); + yield indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, that); + return { HEAD, mindex, iindex, sindex, cindex }; + }); + + this.updateWotbLinks = (cindex) => co(function*() { + for (const entry of cindex) { + const from = yield that.getWrittenIdtyByPubkey(entry.issuer); + const to = yield that.getWrittenIdtyByPubkey(entry.receiver); + if (entry.op == constants.IDX_CREATE) { + that.wotb.addLink(from.wotb_id, to.wotb_id, true); + } else { + // Update = removal + that.wotb.removeLink(from.wotb_id, to.wotb_id, true); + } + } + }); + + this.trimIndexes = (block, conf) => co(function*() { + // TODO: trim should be done on a fork window size + // yield that.cindexDAL.trimExpiredCerts(); + return true; + }); + + this.trimSandboxes = (block, conf) => co(function*() { + yield that.certDAL.trimExpiredCerts(block.medianTime); + yield that.msDAL.trimExpiredMemberships(block.medianTime); + yield that.idtyDAL.trimExpiredIdentities(block.medianTime); + return true; + }); this.savePendingMembership = (ms) => that.msDAL.savePendingMembership(ms); @@ -696,7 +595,6 @@ function FileDAL(params) { this.saveTxsInFiles = (txs, extraProps) => { return Q.all(txs.map((tx) => co(function*() { _.extend(tx, extraProps); - _.extend(tx, {currency: that.getCurrency()}); if (tx.version >= 3) { const sp = tx.blockstamp.split('-'); tx.blockstampTime = (yield that.getBlockByNumberAndHash(sp[0], sp[1])).medianTime; @@ -715,56 +613,13 @@ function FileDAL(params) { return merkle; }); - this.removeLink = (link) => that.linksDAL.removeLink(link); - - this.removeAllSourcesOfBlock = (number) => that.sourcesDAL.removeAllSourcesOfBlock(number); - - this.unConsumeSource = (identifier, noffset) => that.sourcesDAL.unConsumeSource(identifier, noffset); - - this.unflagExpiredIdentitiesOf = (number) => that.idtyDAL.unflagExpiredIdentitiesOf(number); - - this.unflagExpiredCertificationsOf = (number) => that.certDAL.unflagExpiredCertificationsOf(number); - - this.unflagExpiredMembershipsOf = (number) => that.msDAL.unflagExpiredMembershipsOf(number); - - this.saveSource = (src) => that.sourcesDAL.addSource(src.type, src.number, src.identifier, src.noffset, - src.amount, src.base, src.block_hash, src.time, src.conditions); - - this.updateSources = (sources) => that.sourcesDAL.updateBatchOfSources(sources); - - this.updateCertifications = (certs) => that.certDAL.updateBatchOfCertifications(certs); - - this.updateMemberships = (certs) => that.msDAL.updateBatchOfMemberships(certs); - - this.updateLinks = (certs) => that.linksDAL.updateBatchOfLinks(certs); + this.removeAllSourcesOfBlock = (blockstamp) => that.sindexDAL.removeBlock(blockstamp); this.updateTransactions = (txs) => that.txsDAL.insertBatchOfTxs(txs); - this.officializeCertification = (cert) => that.certDAL.saveOfficial(cert); - - this.saveCert = (cert) => - // TODO: create a specific method with a different name and hide saveCert() - that.certDAL.saveCert(cert); - - this.savePendingIdentity = (idty) => - // TODO: create a specific method with a different name and hide saveIdentity() - that.idtyDAL.saveIdentity(idty); - - this.revokeIdentity = (pubkey, number) => that.idtyDAL.revokeIdentity(pubkey, number); + this.savePendingIdentity = (idty) => that.idtyDAL.saveIdentity(idty); - this.unrevokeIdentity = (pubkey) => that.idtyDAL.unrevokeIdentity(pubkey); - - this.excludeIdentity = (pubkey) => that.idtyDAL.excludeIdentity(pubkey); - - this.newIdentity = (idty) => co(function *() { - return that.idtyDAL.newIdentity(idty); - }); - - this.joinIdentity = (pubkey, number) => that.idtyDAL.joinIdentity(pubkey, number); - - this.activeIdentity = (pubkey, number) => that.idtyDAL.activeIdentity(pubkey, number); - - this.leaveIdentity = (pubkey, number) => that.idtyDAL.leaveIdentity(pubkey, number); + this.revokeIdentity = (pubkey) => that.idtyDAL.revokeIdentity(pubkey); this.removeUnWrittenWithPubkey = (pubkey) => co(function*() { return yield that.idtyDAL.removeUnWrittenWithPubkey(pubkey) @@ -774,42 +629,6 @@ function FileDAL(params) { return yield that.idtyDAL.removeUnWrittenWithUID(pubkey); }); - this.unacceptIdentity = that.idtyDAL.unacceptIdentity; - - this.getPreviousMembershipsInfos = (ms) => co(function*() { - const previousMS = yield that.msDAL.previousMS(ms.issuer, ms.number); - let previousIN = previousMS; - if (previousMS.membership !== 'IN') { - previousIN = yield that.msDAL.previousIN(ms.issuer, ms.number); - } - return { - previousIN: previousIN, - previousMS: previousMS - }; - }); - - this.unJoinIdentity = (ms) => co(function *() { - const previousMSS = yield that.getPreviousMembershipsInfos(ms); - yield that.idtyDAL.unJoinIdentity(ms, previousMSS.previousMS, previousMSS.previousIN); - yield that.msDAL.unwriteMS(ms); - }); - - this.unRenewIdentity = (ms) => co(function *() { - const previousMSS = yield that.getPreviousMembershipsInfos(ms); - yield that.idtyDAL.unRenewIdentity(ms, previousMSS.previousMS, previousMSS.previousIN); - yield that.msDAL.unwriteMS(ms); - }); - - this.unLeaveIdentity = (ms) => co(function *() { - const previousMSS = yield that.getPreviousMembershipsInfos(ms); - yield that.idtyDAL.unLeaveIdentity(ms, previousMSS.previousMS, previousMSS.previousIN); - yield that.msDAL.unwriteMS(ms); - }); - - this.unFlagToBeKicked = that.idtyDAL.unFlagToBeKicked.bind(that.idtyDAL); - - this.unExcludeIdentity = that.idtyDAL.unExcludeIdentity; - this.registerNewCertification = (cert) => that.certDAL.saveNewCertification(cert); this.saveTransaction = (tx) => that.txsDAL.addPending(tx); @@ -835,7 +654,7 @@ function FileDAL(params) { }); this.getUDHistory = (pubkey) => co(function *() { - const sources = yield that.sourcesDAL.getUDSources(pubkey); + const sources = yield that.sindexDAL.getUDSources(pubkey); return { history: sources.map((src) => _.extend({ block_number: src.number @@ -853,6 +672,28 @@ function FileDAL(params) { return _.chain(blocks).pluck('issuer').uniq().value(); }); + /** + * Gets a range of entries for the last `start`th to the last `end`th HEAD entry. + * @param start The starting entry number (min. 1) + * @param end The ending entry (max. BINDEX length) + * @param property If provided, transforms the range of entries into an array of the asked property. + */ + this.range = (start, end, property) => co(function*() { + const range = yield that.bindexDAL.range(start, end); + if (property) { + // Filter on a particular property + return range.map((b) => b[property]); + } else { + return range; + } + }); + + /** + * Get the last `n`th entry from the BINDEX. + * @param n The entry number (min. 1). + */ + this.head = (n) => this.bindexDAL.head(n); + /*********************** * CONFIGURATION **********************/ diff --git a/app/lib/dal/fileDALs/IndicatorsDAL.js b/app/lib/dal/fileDALs/IndicatorsDAL.js index 433011f1e60ca66fc38430a84673c519151feab8..64b2a93282bb10ad8b18a76d76b5ea5c5be8d17a 100644 --- a/app/lib/dal/fileDALs/IndicatorsDAL.js +++ b/app/lib/dal/fileDALs/IndicatorsDAL.js @@ -23,38 +23,5 @@ function IndicatorsDAL(rootPath, qioFS, parentCore, localDAL, AbstractStorage) { }); }; - const cache = {}; - function setBlock(key, block) { - cache[key] = block; - return Promise.resolve(block); - } - - function getBlock(key) { - return Promise.resolve(cache[key] || null); - } - - this.writeCurrentExcluding = (excluding) => setBlock('excludingMS', excluding); - - this.writeCurrentRevocating = (revocating) => setBlock('revocatingMS', revocating); - - this.writeCurrentExcludingForCert = (excluding) => setBlock('excludingCRT', excluding); - - this.writeCurrentExpiringForCert = (excluding) => setBlock('expiringCRT', excluding); - - this.writeCurrentExpiringForIdty = (excluding) => setBlock('expiringIDTY', excluding); - - this.writeCurrentExpiringForMembership = (excluding) => setBlock('expiringMS', excluding); - - this.getCurrentMembershipExcludingBlock = () => getBlock('excludingMS'); - - this.getCurrentMembershipRevocatingBlock = () => getBlock('revocatingMS'); - - this.getCurrentCertificationExpiringBlock = () => getBlock('expiringCRT'); - - this.getCurrentCertificationExcludingBlock = () => getBlock('excludingCRT'); - - this.getCurrentIdentityExpiringBlock = () => getBlock('expiringIDTY'); - - this.getCurrentMembershipExpiringBlock = () => getBlock('expiringMS'); } diff --git a/app/lib/dal/sqliteDAL/AbstractSQLite.js b/app/lib/dal/sqliteDAL/AbstractSQLite.js index 22580329cb8ab1016dd2fc34cba055a3458355e5..cf2ea2f65ec2ae4af071861f65d9fffe35ee15c7 100644 --- a/app/lib/dal/sqliteDAL/AbstractSQLite.js +++ b/app/lib/dal/sqliteDAL/AbstractSQLite.js @@ -2,7 +2,6 @@ * Created by cgeek on 22/08/15. */ -const Q = require('q'); const _ = require('underscore'); const co = require('co'); const colors = require('colors'); @@ -73,16 +72,6 @@ function AbstractSQLite(driver) { return that.query('SELECT * FROM ' + that.table + ' WHERE ' + keys.map((k) => '`' + k + '` like ?').join(' or '), keys.map((k) => obj[k].toUpperCase()), sort); }); - this.sqlUpdateWhere = (obj, where) => co(function *() { - // Valorizations - const setInstructions = toSetArray(obj).join(', '); - const setValues = toParams(obj); - // Conditions - const conditions = toConditionsArray(where).join(' AND '); - const condValues = toParams(where); - return that.query('UPDATE ' + that.table + ' SET ' + setInstructions + ' WHERE ' + conditions, setValues.concat(condValues)); - }); - this.sqlRemoveWhere = (obj) => co(function *() { const keys = _.keys(obj); return that.query('DELETE FROM ' + that.table + ' WHERE ' + keys.map((k) => '`' + k + '` = ?').join(' and '), keys.map((k) => obj[k])); @@ -107,17 +96,16 @@ function AbstractSQLite(driver) { yield that.insert(toSave); }); - this.updateEntity = (entity, values) => co(function *() { - const toSave = toRow(entity); - if (that.beforeSaveHook) { - that.beforeSaveHook(toSave); - } - const valuesKeys = _.keys(values); - const valorizations = valuesKeys.map((field) => '`' + field + '` = ?').join(', '); + this.insert = (entity) => co(function *() { + const row = toRow(entity); + const values = that.fields.map((f) => row[f]); + yield that.query(that.getInsertQuery(), values); + }); + + this.getEntity = (entity) => co(function *() { const conditions = getPKFields().map((field) => '`' + field + '` = ?').join(' and '); - const setValues = valuesKeys.map((field) => values[field]); - const condValues = getPKFields().map((k) => toSave[k]); - return that.query('UPDATE ' + that.table + ' SET ' + valorizations + ' WHERE ' + conditions, setValues.concat(condValues)); + const params = toParams(entity, getPKFields()); + return (yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + conditions, params))[0]; }); this.deleteEntity = (entity) => co(function *() { @@ -130,18 +118,6 @@ function AbstractSQLite(driver) { return that.query('DELETE FROM ' + that.table + ' WHERE ' + conditions, condValues); }); - this.insert = (entity) => co(function *() { - const row = toRow(entity); - const values = that.fields.map((f) => row[f]); - yield that.query(that.getInsertQuery(), values); - }); - - this.getEntity = (entity) => co(function *() { - const conditions = getPKFields().map((field) => '`' + field + '` = ?').join(' and '); - const params = toParams(entity, getPKFields()); - return (yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + conditions, params))[0]; - }); - this.exec = (sql) => co(function *() { try { // logger.trace(sql); @@ -170,38 +146,6 @@ function AbstractSQLite(driver) { return "(" + values.join(',') + ")"; }; - this.getUpdateRawQuery = (toSave, values) => { - if (that.beforeSaveHook) { - that.beforeSaveHook(toSave); - } - const valuesKeys = _.keys(values); - const valorizations = valuesKeys.map((field) => '`' + field + '` = ' + escapeToSQLite(values[field])).join(', '); - const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and '); - return 'UPDATE ' + that.table + ' SET ' + valorizations + ' WHERE ' + conditions + ';'; - }; - - this.getDeleteRawQuery = (toSave) => { - if (that.beforeSaveHook) { - that.beforeSaveHook(toSave); - } - const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and '); - return 'DELETE FROM ' + that.table + ' WHERE ' + conditions + ';'; - }; - - this.getConsumeHead = () => { - return 'UPDATE ' + that.table + " SET consumed = 1 WHERE "; - }; - - this.getConsumeValues = (entities) => { - return entities.map((toSave) => { - if (that.beforeSaveHook) { - that.beforeSaveHook(toSave); - } - const conditions = getPKFields().map((field) => '`' + field + '` = ' + escapeToSQLite(toSave[field])).join(' and '); - return "(" + conditions + ")"; - }).join(' OR\n '); - }; - this.toInsertValues = (entity) => { const row = toRow(entity); const values = that.fields.map((f) => row[f]); @@ -209,6 +153,22 @@ function AbstractSQLite(driver) { return "(" + formatted.join(',') + ")"; }; + /** + * Make a batch insert. + * @param records The records to insert as a batch. + */ + this.insertBatch = (records) => co(function *() { + const queries = []; + if (records.length) { + const insert = that.getInsertHead(); + const values = records.map((src) => that.getInsertValue(src)); + queries.push(insert + '\n' + values.join(',\n') + ';'); + } + if (queries.length) { + return that.exec(queries.join('\n')); + } + }); + function toConditionsArray(obj) { return _.keys(obj).map((k) => { if (obj[k].$lte !== undefined) { @@ -216,7 +176,7 @@ function AbstractSQLite(driver) { } else if (obj[k].$gte !== undefined) { return '`' + k + '` >= ?'; } else if (obj[k].$gt !== undefined) { - return '`' + k + '` >= ?'; + return '`' + k + '` > ?'; } else if (obj[k].$lt !== undefined) { return '`' + k + '` < ?'; } else if (obj[k].$null !== undefined) { @@ -229,11 +189,6 @@ function AbstractSQLite(driver) { }); } - const toSetArray= (obj) => { - const row = toRow(obj); - return _.keys(row).map((k) => '`' + k + '` = ?'); - }; - const toParams = (obj, fields) => { let params = []; (fields || _.keys(obj)).forEach((f) => { @@ -259,7 +214,11 @@ function AbstractSQLite(driver) { const escapeToSQLite = (val) => { if (typeof val == "boolean") { // SQLite specific: true => 1, false => 0 - return val ? 1 : 0; + if (val !== null && val !== undefined) { + return val ? 1 : 0; + } else { + return null; + } } else if (typeof val == "string") { return "'" + val.replace(/'/g, "\\'") + "'"; @@ -295,7 +254,7 @@ function AbstractSQLite(driver) { } // Booleans for (const f of that.booleans) { - row[f] = Boolean(row[f]); + row[f] = row[f] !== null ? Boolean(row[f]) : null; } // Transient for (const f of (that.transientFields || [])) { @@ -324,5 +283,5 @@ function AbstractSQLite(driver) { row[toTranslate[objField]] = row[objField]; } return row; - }; + } } \ No newline at end of file diff --git a/app/lib/dal/sqliteDAL/BlockDAL.js b/app/lib/dal/sqliteDAL/BlockDAL.js index f00f368eb9616976f7a991a7307717d0c80526ca..647d6bc59a1fb3c044204ddb4ca4de5872e042c9 100644 --- a/app/lib/dal/sqliteDAL/BlockDAL.js +++ b/app/lib/dal/sqliteDAL/BlockDAL.js @@ -108,15 +108,6 @@ function BlockDAL(driver) { return res[0].quantity; }); - this.getNbIssuedFrom = (issuer, from) => co(function *() { - let res = yield that.query('SELECT COUNT(*) as quantity FROM block WHERE issuer = ? and number >= ? and NOT fork', [issuer, from]); - return res[0].quantity; - }); - - this.getMoreRecentBlockWithTimeEqualBelow = (maxTime) => co(function *() { - return (yield that.query('SELECT * FROM block WHERE medianTime <= ? and NOT fork ORDER BY number DESC LIMIT 1', [maxTime]))[0]; - }); - this.getForkBlocks = () => { return that.query('SELECT * FROM block WHERE fork ORDER BY number'); }; @@ -125,10 +116,6 @@ function BlockDAL(driver) { return that.query('SELECT * FROM block WHERE dividend IS NOT NULL ORDER BY number'); }; - this.nextBlockWithDifferentMedianTime = (block) => co(function *() { - return (yield that.query('SELECT * FROM block WHERE number > ? AND medianTime > ? and NOT fork ORDER BY number ASC LIMIT 1', [block.number, block.medianTime]))[0]; - }); - this.saveBunch = (blocks) => co(function *() { let queries = "INSERT INTO block (" + that.fields.join(',') + ") VALUES "; for (let i = 0, len = blocks.length; i < len; i++) { diff --git a/app/lib/dal/sqliteDAL/CertDAL.js b/app/lib/dal/sqliteDAL/CertDAL.js index f38cb8d06ca9cb2a524247c52d441bca074c6388..195168476162e7001f00c60bc0e05582e53b372d 100644 --- a/app/lib/dal/sqliteDAL/CertDAL.js +++ b/app/lib/dal/sqliteDAL/CertDAL.js @@ -31,7 +31,8 @@ function CertDAL(driver) { 'to', 'from', 'block', - 'expired' + 'expired', + 'expires_on' ]; this.arrays = []; this.booleans = ['linked', 'written']; @@ -52,6 +53,7 @@ function CertDAL(driver) { 'written BOOLEAN NOT NULL,' + 'written_block INTEGER,' + 'written_hash VARCHAR(64),' + + 'expires_on INTEGER NULL,' + 'PRIMARY KEY (`from`, target, sig, written_block)' + ');' + 'CREATE INDEX IF NOT EXISTS idx_cert_from ON cert (`from`);' + @@ -68,7 +70,7 @@ function CertDAL(driver) { target: hash }); - this.getFromPubkey = (pubkey) => this.sqlFind({ + this.getFromPubkeyCerts = (pubkey) => this.sqlFind({ from: pubkey }); @@ -81,43 +83,13 @@ function CertDAL(driver) { linked: false }); - this.listLocalPending = () => Q([]); - - this.saveOfficial = (cert) => { - cert.linked = true; - return this.saveEntity(cert); - }; - - this.saveCert = (cert) => this.saveEntity(cert); - this.saveNewCertification = (cert) => this.saveEntity(cert); this.existsGivenCert = (cert) => Q(this.sqlExisting(cert)); - this.updateBatchOfCertifications = (certs) => co(function *() { - const queries = []; - const insert = that.getInsertHead(); - const values = certs.map((cert) => that.getInsertValue(cert)); - if (certs.length) { - queries.push(insert + '\n' + values.join(',\n') + ';'); - } - if (queries.length) { - return that.exec(queries.join('\n')); - } - }); - - this.flagExpiredCertifications = (maxNumber, onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = ' + onNumber + ' ' + - 'WHERE expired IS NULL ' + - 'AND block_number <= ' + maxNumber); - }); + this.deleteCert = (cert) => this.deleteEntity(cert); - this.unflagExpiredCertificationsOf = (onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = NULL ' + - 'WHERE expired = ' + onNumber); - }); + this.trimExpiredCerts = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime); /************************** * SANDBOX STUFF diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.js b/app/lib/dal/sqliteDAL/IdentityDAL.js index 5fc298fda9decf41ef51338b4f9c9c66eb7351fa..8befe1751ad1aa259ae46e77d669ee6f1338b9c0 100644 --- a/app/lib/dal/sqliteDAL/IdentityDAL.js +++ b/app/lib/dal/sqliteDAL/IdentityDAL.js @@ -11,7 +11,7 @@ const SandBox = require('./SandBox'); module.exports = IdentityDAL; -function IdentityDAL(driver, wotb) { +function IdentityDAL(driver) { "use strict"; @@ -37,7 +37,8 @@ function IdentityDAL(driver, wotb) { 'hash', 'written', 'wotb_id', - 'expired' + 'expired', + 'expires_on' ]; this.arrays = []; this.booleans = ['revoked', 'member', 'kick', 'leaving', 'wasMember', 'written']; @@ -49,20 +50,21 @@ function IdentityDAL(driver, wotb) { return that.exec('BEGIN;' + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + 'revoked BOOLEAN NOT NULL,' + - 'currentMSN INTEGER NOT NULL,' + - 'currentINN INTEGER NOT NULL,' + + 'currentMSN INTEGER NULL,' + + 'currentINN INTEGER NULL,' + 'buid VARCHAR(100) NOT NULL,' + 'member BOOLEAN NOT NULL,' + 'kick BOOLEAN NOT NULL,' + - 'leaving BOOLEAN NOT NULL,' + + 'leaving BOOLEAN NULL,' + 'wasMember BOOLEAN NOT NULL,' + 'pubkey VARCHAR(50) NOT NULL,' + 'uid VARCHAR(255) NOT NULL,' + 'sig VARCHAR(100) NOT NULL,' + 'revocation_sig VARCHAR(100) NULL,' + 'hash VARCHAR(64) NOT NULL,' + - 'written BOOLEAN NOT NULL,' + + 'written BOOLEAN NULL,' + 'wotb_id INTEGER NULL,' + + 'expires_on INTEGER NULL,' + 'PRIMARY KEY (pubkey,uid,hash)' + ');' + 'CREATE INDEX IF NOT EXISTS idx_idty_pubkey ON idty (pubkey);' + @@ -77,131 +79,7 @@ function IdentityDAL(driver, wotb) { 'COMMIT;', []); }); - this.revokeIdentity = (pubkey, number) => { - return co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.revoked = true; - idty.revoked_on = number; - return that.saveIdentity(idty); - }); - }; - - this.unrevokeIdentity = (pubkey) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.revoked = false; - idty.revoked_on = null; - return that.saveIdentity(idty); - }); - - this.excludeIdentity = (pubkey) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.member = false; - idty.kick = false; - wotb.setEnabled(false, idty.wotb_id); - return that.saveIdentity(idty); - }); - - this.unacceptIdentity = (pubkey) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.currentMSN = -1; - idty.currentINN = -1; - idty.written = false; - idty.wasMember = false; - idty.member = false; - idty.kick = false; - idty.leaving = false; - idty.wotb_id = wotb.removeNode(); - return that.saveIdentity(idty); - }); - - this.unJoinIdentity = (ms, previousMS, previousIN) => co(function *() { - const idty = yield that.getFromPubkey(ms.issuer); - idty.currentMSN = previousMS.number; - idty.currentINN = previousIN.number; - if (previousMS.membership === 'OUT') { - idty.leaving = true; - } - idty.member = false; - /** - * Note: it is not required to do: - * - * `idty.wasMember = false;` - * - * because this is already done by `unacceptIdentity` method. - */ - wotb.setEnabled(false, idty.wotb_id); - return that.saveIdentity(idty); - }); - - this.unRenewIdentity = (ms, previousMS, previousIN) => co(function *() { - const idty = yield that.getFromPubkey(ms.issuer); - idty.currentMSN = previousMS.number; - idty.currentINN = previousIN.number; - if (previousMS.membership === 'OUT') { - idty.leaving = true; - } - return that.saveIdentity(idty); - }); - - this.unLeaveIdentity = (ms, previousMS, previousIN) => co(function *() { - const idty = yield that.getFromPubkey(ms.issuer); - idty.currentMSN = previousMS.number; - idty.currentINN = previousIN.number; - idty.leaving = false; - if (previousMS.membership === 'OUT') { - idty.leaving = true; - } - return that.saveIdentity(idty); - }); - - this.unExcludeIdentity = (pubkey, causeWasRevocation) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.member = true; - idty.kick = !causeWasRevocation; - wotb.setEnabled(true, idty.wotb_id); - return that.saveIdentity(idty); - }); - - this.newIdentity = function(idty) { - idty.currentMSN = -1; // Will be overidden by joinIdentity() - idty.currentINN = -1; // Will be overidden by joinIdentity() - idty.member = true; - idty.wasMember = true; - idty.kick = false; - idty.written = true; - idty.wotb_id = wotb.addNode(); - logger.trace('%s was affected wotb_id %s', idty.uid, idty.wotb_id); - return that.saveIdentity(idty); - }; - - this.joinIdentity = (pubkey, currentMSN) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.currentMSN = currentMSN; - idty.currentINN = currentMSN; - idty.member = true; - idty.wasMember = true; - idty.leaving = false; - wotb.setEnabled(true, idty.wotb_id); - return that.saveIdentity(idty); - }); - - this.activeIdentity = (pubkey, currentMSN) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.currentMSN = currentMSN; - idty.currentINN = currentMSN; - idty.member = true; - idty.kick = false; - idty.leaving = false; - wotb.setEnabled(true, idty.wotb_id); - return that.saveIdentity(idty); - }); - - this.leaveIdentity = (pubkey, currentMSN) => co(function *() { - const idty = yield that.getFromPubkey(pubkey); - idty.currentMSN = currentMSN; - idty.leaving = true; - return that.saveIdentity(idty); - }); + this.revokeIdentity = (pubkey) => this.exec('DELETE FROM ' + this.table + ' WHERE pubkey = \'' + pubkey + '\''); this.removeUnWrittenWithPubkey = (pubkey) => this.sqlRemoveWhere({ pubkey: pubkey, @@ -213,33 +91,13 @@ function IdentityDAL(driver, wotb) { written: false }); - this.getFromPubkey = (pubkey) => this.sqlFindOne({ - pubkey: pubkey, - wasMember: true - }); - - this.getFromUID = (uid) => this.sqlFindOne({ - uid: uid, - wasMember: true - }); - this.getByHash = (hash) => this.sqlFindOne({ hash: hash }); - this.getLatestMember = () => that.sqlFindOne({ - wasMember: true - }, { - wotb_id: this.DESC - }); - this.saveIdentity = (idty) => this.saveEntity(idty); - this.getWhoIsOrWasMember = () => that.sqlFind({ - wasMember: true - }); - this.getToRevoke = () => that.sqlFind({ revocation_sig: { $null: false }, revoked: false, @@ -247,56 +105,16 @@ function IdentityDAL(driver, wotb) { }); this.getPendingIdentities = () => that.sqlFind({ - wasMember: false + revocation_sig: { $null: false }, + revoked: false }); - this.listLocalPending = () => Q([]); - this.searchThoseMatching = (search) => that.sqlFindLikeAny({ pubkey: "%" + search + "%", uid: "%" + search + "%" }); - this.flagExpiredIdentities = (maxNumber, onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = ' + onNumber + ' ' + - 'WHERE expired IS NULL ' + - 'AND CAST(SUBSTR(buid, 0, INSTR(buid, "-")) as number) <= ' + maxNumber); - }); - - this.unflagExpiredIdentitiesOf = (onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = NULL ' + - 'WHERE expired = ' + onNumber); - }); - - this.unFlagToBeKicked = () => that.exec('UPDATE ' + that.table + ' SET kick = 0 WHERE kick'); - - this.kickMembersForMembershipBelow = (maxNumber) => co(function *() { - const toKick = yield that.sqlFind({ - currentINN: { $lte: maxNumber }, - kick: false, - member: true - }); - for (const idty of toKick) { - logger.trace('Kick %s for currentINN <= %s', idty.uid, maxNumber); - idty.kick = true; - yield that.saveEntity(idty); - } - }); - - this.revokeMembersForMembershipBelow = (maxNumber) => co(function *() { - const toKick = yield that.sqlFind({ - currentINN: { $lte: maxNumber }, - kick: false, - member: true - }); - for (const idty of toKick) { - logger.trace('Revoke %s for currentINN <= %s', idty.uid, maxNumber); - idty.revoked = true; - yield that.saveEntity(idty); - } - }); + this.trimExpiredIdentities = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime); /************************** * SANDBOX STUFF diff --git a/app/lib/dal/sqliteDAL/LinksDAL.js b/app/lib/dal/sqliteDAL/LinksDAL.js deleted file mode 100644 index 18aece48a1eeff4e6d62fa36adbbbb4a7145be88..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/LinksDAL.js +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Created by cgeek on 22/08/15. - */ - -const Q = require('q'); -const co = require('co'); -const logger = require('../../logger')('linksDAL'); -const AbstractSQLite = require('./AbstractSQLite'); - -module.exports = LinksDAL; - -function LinksDAL(driver, wotb) { - - "use strict"; - - AbstractSQLite.call(this, driver); - - const that = this; - - this.table = 'link'; - this.fields = [ - 'source', - 'target', - 'timestamp', - 'block_number', - 'block_hash', - 'obsolete', - 'from_wotb_id', - 'to_wotb_id' - ]; - this.arrays = []; - this.booleans = ['obsolete']; - this.pkFields = ['source', 'target', 'block_number', 'block_hash']; - this.translated = {}; - - this.init = () => co(function *() { - return that.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + - 'source VARCHAR(50) NOT NULL,' + - 'target VARCHAR(50) NOT NULL,' + - 'timestamp INTEGER NOT NULL,' + - 'block_number INTEGER NOT NULL,' + - 'block_hash VARCHAR(64),' + - 'obsolete BOOLEAN NOT NULL,' + - 'from_wotb_id INTEGER NULL,' + - 'to_wotb_id INTEGER NULL,' + - 'PRIMARY KEY (source,target,block_number,block_hash)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_link_source ON link (source);' + - 'CREATE INDEX IF NOT EXISTS idx_link_obsolete ON link (obsolete);' + - 'CREATE INDEX IF NOT EXISTS idx_link_target ON link (target);' + - 'CREATE INDEX IF NOT EXISTS idx_link_timestamp ON link (timestamp);' + - 'COMMIT;', []); - }); - - this.getValidLinksFrom = (pubkey) => this.sqlFind({ - source: pubkey, - obsolete: false - }); - - this.getSimilarLinksFromDate = (from, to, minDate) => this.sqlFind({ - source: from, - target: to, - timestamp: { $gte: minDate } - }); - - this.getValidLinksTo = (pubkey) => this.sqlFind({ - target: pubkey, - obsolete: false - }); - - this.getLinksWithPath = (from, to) => - this.sqlFind({ - source: from, - target: to - }); - - this.getLinksFrom = (from) => - this.sqlFind({ - source: from - }); - - this.getLinksOfIssuerAbove = (from, aboveBlock) => - this.sqlFind({ - source: from, - block_number: { $gt: aboveBlock } - }); - - this.obsoletesLinks = (minTimestamp) => co(function *() { - const linksToObsolete = yield that.sqlFind({ - timestamp: { $lte: minTimestamp }, - obsolete: false - }); - linksToObsolete.forEach((link) => wotb.removeLink(link.from_wotb_id, link.to_wotb_id, true)); - return that.sqlUpdateWhere({ obsolete: true }, { - timestamp: { $lte: minTimestamp }, - obsolete: false - }); - }); - - this.unObsoletesLinks = (minTimestamp) => co(function *() { - const linksToUnObsolete = yield that.sqlFind({ - timestamp: { $gte: minTimestamp }, - obsolete: true - }); - linksToUnObsolete.forEach((link) => wotb.addLink(link.from_wotb_id, link.to_wotb_id)); - return that.sqlUpdateWhere({ obsolete: false }, { - timestamp: { $gte: minTimestamp } - }); - }); - - this.removeLink = (link) => co(function *() { - wotb.removeLink(link.from_wotb_id, link.to_wotb_id); - return that.deleteEntity(link); - }); - - this.updateBatchOfLinks = (links) => co(function *() { - const queries = []; - const insert = that.getInsertHead(); - const values = links.map((link) => { - wotb.addLink(link.from_wotb_id, link.to_wotb_id); - return that.getInsertValue(link); - }); - if (links.length) { - queries.push(insert + '\n' + values.join(',\n') + ';'); - logger.query(queries.join('\n')); - } - if (queries.length) { - return that.exec(queries.join('\n')); - } - }); -} \ No newline at end of file diff --git a/app/lib/dal/sqliteDAL/MembershipDAL.js b/app/lib/dal/sqliteDAL/MembershipDAL.js index 261b3fc1d7bcb4824977e2f75e651c6c8afc4582..d4338a4ce89ce1fa85debd2b3456f1546c8b7119 100644 --- a/app/lib/dal/sqliteDAL/MembershipDAL.js +++ b/app/lib/dal/sqliteDAL/MembershipDAL.js @@ -33,6 +33,7 @@ function MembershipDAL(driver) { 'idtyHash', 'written', 'written_number', + 'expires_on', 'signature', 'expired' ]; @@ -56,6 +57,7 @@ function MembershipDAL(driver) { 'idtyHash VARCHAR(64),' + 'written BOOLEAN NOT NULL,' + 'written_number INTEGER,' + + 'expires_on INTEGER NULL,' + 'signature VARCHAR(50),' + 'PRIMARY KEY (issuer,signature)' + ');' + @@ -65,112 +67,32 @@ function MembershipDAL(driver) { 'COMMIT;', []); }); - this.getMembershipOfIssuer = (ms) => this.sqlExisting(ms); - this.getMembershipsOfIssuer = (issuer) => this.sqlFind({ issuer: issuer }); - this.getPendingINOfTarget = (hash) => - this.sqlFind({ - idtyHash: hash, - membership: 'IN', - written: false + this.getPendingINOfTarget = (hash) => this.sqlFind({ + idtyHash: hash, + membership: 'IN' }); this.getPendingIN = () => this.sqlFind({ - membership: 'IN', - written: false + membership: 'IN' }); this.getPendingOUT = () => this.sqlFind({ - membership: 'OUT', - written: false - }); - - this.previousMS = (pubkey, maxNumber) => co(function *() { - let previous = yield that.sqlFindOne({ - issuer: pubkey, - number: { $lt: maxNumber }, - written: true - }, { - number: 'DESC' - }); - if (!previous) { - previous = { - number: -1 - }; - } - return previous; - }); - - this.previousIN = (pubkey, maxNumber) => co(function *() { - let previous = yield that.sqlFindOne({ - issuer: pubkey, - membership: 'IN', - number: { $lt: maxNumber }, - written: true - }, { - number: 'DESC' - }); - if (!previous) { - previous = { - number: -1 - }; - } - return previous; - }); - - this.unwriteMS = (ms) => co(function *() { - const existing = yield that.sqlExisting({ - issuer: ms.issuer, - signature: ms.signature - }); - if (existing) { - existing.written = false; - existing.written_number = null; - that.saveEntity(existing); - } + membership: 'OUT' }); - this.saveOfficialMS = (type, ms, blockNumber) => { - const obj = _.extend({}, ms); - obj.membership = type.toUpperCase(); - obj.written = true; - obj.written_number = blockNumber; - return this.saveEntity(_.pick(obj, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'written', 'written_number', 'signature')); - }; - this.savePendingMembership = (ms) => { ms.membership = ms.membership.toUpperCase(); ms.written = false; - return this.saveEntity(_.pick(ms, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'written', 'written_number', 'signature')); + return this.saveEntity(_.pick(ms, 'membership', 'issuer', 'number', 'blockNumber', 'blockHash', 'userid', 'certts', 'block', 'fpr', 'idtyHash', 'expires_on', 'written', 'written_number', 'signature')); }; - this.updateBatchOfMemberships = (mss) => co(function *() { - const queries = []; - const insert = that.getInsertHead(); - const values = mss.map((cert) => that.getInsertValue(cert)); - if (mss.length) { - queries.push(insert + '\n' + values.join(',\n') + ';'); - } - if (queries.length) { - return that.exec(queries.join('\n')); - } - }); - - this.flagExpiredMemberships = (maxNumber, onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = ' + onNumber + ' ' + - 'WHERE expired IS NULL ' + - 'AND blockNumber <= ' + maxNumber); - }); + this.deleteMS = (ms) => this.deleteEntity(ms); - this.unflagExpiredMembershipsOf = (onNumber) => co(function *() { - yield that.exec('UPDATE ' + that.table + ' ' + - 'SET expired = NULL ' + - 'WHERE expired = ' + onNumber); - }); + this.trimExpiredMemberships = (medianTime) => this.exec('DELETE FROM ' + this.table + ' WHERE expires_on IS NULL OR expires_on < ' + medianTime); /************************** * SANDBOX STUFF diff --git a/app/lib/dal/sqliteDAL/MetaDAL.js b/app/lib/dal/sqliteDAL/MetaDAL.js index dfbcfaf48d82f4272b138dfe73e0c2126021890b..170df720b140b82ba7256afd86cd1b1b16cff602 100644 --- a/app/lib/dal/sqliteDAL/MetaDAL.js +++ b/app/lib/dal/sqliteDAL/MetaDAL.js @@ -162,13 +162,14 @@ function MetaDAL(driver) { 15: () => co(function *() { let blockDAL = new (require('./BlockDAL'))(driver); let idtyDAL = new (require('./IdentityDAL'))(driver); + let iindexDAL = new (require('./index/IIndexDAL'))(driver); yield idtyDAL.exec('ALTER TABLE idty ADD COLUMN revoked_on INTEGER NULL'); const blocks = yield blockDAL.query('SELECT * FROM block WHERE revoked NOT LIKE ?', ['[]']); for (const block of blocks) { // Explicit revocations only for (const inlineRevocation of block.revoked) { const revocation = Revocation.statics.fromInline(inlineRevocation); - const idty = yield idtyDAL.getFromPubkey(revocation.pubkey); + const idty = yield iindexDAL.getFromPubkey(revocation.pubkey); idty.revoked_on = block.number; yield idtyDAL.saveIdentity(idty); } diff --git a/app/lib/dal/sqliteDAL/SourcesDAL.js b/app/lib/dal/sqliteDAL/SourcesDAL.js deleted file mode 100644 index 7e429aad129eba0c9b1732cbc139c68ddba5537b..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/SourcesDAL.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Created by cgeek on 22/08/15. - */ - -const co = require('co'); -const _ = require('underscore'); -const AbstractSQLite = require('./AbstractSQLite'); - -module.exports = SourcesDAL; - -function SourcesDAL(driver) { - - "use strict"; - - AbstractSQLite.call(this, driver); - - const that = this; - - this.table = 'source'; - this.fields = [ - 'type', - 'number', - 'time', - 'identifier', - 'amount', - 'base', - 'noffset', - 'block_hash', - 'consumed', - 'conditions' - ]; - this.arrays = []; - this.bigintegers = ['amount']; - this.booleans = ['consumed']; - this.pkFields = ['identifier', 'noffset']; - this.translated = {}; - - this.init = () => co(function *() { - return that.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + - 'type VARCHAR(1) NOT NULL,' + - 'number INTEGER NOT NULL,' + - 'time DATETIME,' + - 'identifier VARCHAR(64) NOT NULL,' + - 'noffset INTEGER NOT NULL,' + - 'amount VARCHAR(50) NOT NULL,' + - 'base INTEGER NOT NULL,' + - 'block_hash VARCHAR(64) NOT NULL,' + - 'consumed BOOLEAN NOT NULL,' + - 'conditions TEXT,' + - 'PRIMARY KEY (identifier,noffset)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_source_type ON source (type);' + - 'CREATE INDEX IF NOT EXISTS idx_source_number ON source (number);' + - 'CREATE INDEX IF NOT EXISTS idx_source_identifier ON source (identifier);' + - 'CREATE INDEX IF NOT EXISTS idx_source_noffset ON source (noffset);' + - 'CREATE INDEX IF NOT EXISTS idx_source_conditions ON source (conditions);' + - 'COMMIT;', []); - }); - - this.getAvailableForPubkey = (pubkey) => this.sqlFind({ - conditions: { $contains: pubkey }, - consumed: false - }); - - this.getUDSources = (pubkey) => this.sqlFind({ - conditions: { $contains: pubkey }, - type: 'D' - }); - - this.getSource = (identifier, index) => this.sqlFindOne({ - identifier: identifier, - noffset: index - }); - - this.getSource = (identifier, noffset) => that.sqlExisting({ - identifier: identifier, - noffset: noffset - }); - - this.consumeSource = (identifier, index) => co(function *() { - return that.updateEntity({ - identifier: identifier, - noffset: index - },{ - consumed: true - }); - }); - - this.addSource = (type, number, identifier, noffset, amount, base, block_hash, time, conditions) => this.saveEntity({ - type: type, - number: number, - identifier: identifier, - noffset: noffset, - amount: amount, - base: base, - time: time, - block_hash: block_hash, - consumed: false, - conditions: conditions - }); - - this.unConsumeSource = (identifier, index) => co(function *() { - let src = yield that.sqlExisting({ - identifier: identifier, - noffset: index - }); - if (!src) { - throw "Cannot revert: inputs used by the blocks were removed from the DB"; - } else { - return that.updateEntity({ - identifier: identifier, - noffset: index - },{ - consumed: false - }); - } - }); - - this.updateBatchOfSources = (sources) => co(function *() { - const inserts = _.filter(sources, { toConsume: false }); - const updates = _.filter(sources, { toConsume: true }); - const queries = []; - if (inserts.length) { - const insert = that.getInsertHead(); - const values = inserts.map((src) => that.getInsertValue(_.extend(src, { consumed: false }))); - queries.push(insert + '\n' + values.join(',\n') + ';'); - } - if (updates.length) { - const del = that.getConsumeHead(); - const values = that.getConsumeValues(updates); - queries.push(del + '\n' + values + ';'); - } - if (queries.length) { - return that.exec(queries.join('\n')); - } - }); - - this.removeAllSourcesOfBlock = (number) => this.sqlRemoveWhere({ - number: number - }); -} diff --git a/app/lib/dal/sqliteDAL/index/BIndexDAL.js b/app/lib/dal/sqliteDAL/index/BIndexDAL.js new file mode 100644 index 0000000000000000000000000000000000000000..147c4e322f40d626af5cd0c141e27f0924255d73 --- /dev/null +++ b/app/lib/dal/sqliteDAL/index/BIndexDAL.js @@ -0,0 +1,100 @@ +/** + * Created by cgeek on 22/08/15. + */ + +const co = require('co'); +const _ = require('underscore'); +const AbstractSQLite = require('./../AbstractSQLite'); + +module.exports = BIndexDAL; + +function BIndexDAL(driver) { + + "use strict"; + + AbstractSQLite.call(this, driver); + + const that = this; + + this.table = 'b_index'; + this.fields = [ + 'version', + 'bsize', + 'hash', + 'issuer', + 'time', + 'number', + 'membersCount', + 'issuersCount', + 'issuersFrame', + 'issuersFrameVar', + 'issuerDiff', + 'avgBlockSize', + 'medianTime', + 'dividend', + 'mass', + 'unitBase', + 'powMin', + 'udTime', + 'diffNumber', + 'speed' + ]; + this.arrays = []; + this.bigintegers = ['mass']; + this.booleans = ['leaving']; + this.pkFields = ['number']; + this.translated = {}; + + this.init = () => co(function *() { + return that.exec('BEGIN;' + + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + + 'version INTEGER NOT NULL,' + + 'bsize INTEGER NOT NULL,' + + 'hash VARCHAR(64) NOT NULL,' + + 'issuer VARCHAR(50) NOT NULL,' + + 'time INTEGER NOT NULL,' + + 'number INTEGER NOT NULL,' + + 'membersCount INTEGER NOT NULL,' + + 'issuersCount INTEGER NOT NULL,' + + 'issuersFrame INTEGER NOT NULL,' + + 'issuersFrameVar INTEGER NOT NULL,' + + 'issuerDiff INTEGER NULL,' + + 'avgBlockSize INTEGER NOT NULL,' + + 'medianTime INTEGER NOT NULL,' + + 'dividend INTEGER NOT NULL,' + + 'mass VARCHAR(100) NOT NULL,' + + 'unitBase INTEGER NOT NULL,' + + 'powMin INTEGER NOT NULL,' + + 'udTime INTEGER NOT NULL,' + + 'diffNumber INTEGER NOT NULL,' + + 'speed FLOAT NOT NULL,' + + 'PRIMARY KEY (number)' + + ');' + + 'CREATE INDEX IF NOT EXISTS idx_bindex_number ON b_index (number);' + + 'CREATE INDEX IF NOT EXISTS idx_bindex_issuer ON b_index (issuer);' + + 'COMMIT;', []); + }); + + /** + * Get HEAD~n + * @param n Position + */ + this.head = (n) => co(function*() { + // Default to HEAD~1 + n = n || 1; + const headRecords = yield that.query('SELECT * FROM ' + that.table + ' ORDER BY number DESC LIMIT 1 OFFSET ?', [n - 1]); + return headRecords[0]; + }); + + /** + * Get HEAD~n..m + * @param n + * @param m + */ + this.range = (n, m) => co(function*() { + const count = m - n + 1; + return that.query('SELECT * FROM ' + that.table + ' ORDER BY number DESC LIMIT ? OFFSET ?', [count, n - 1]); + }); + + this.removeBlock = (number) => that.exec('DELETE FROM ' + that.table + ' WHERE number = ' + number); +} diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.js b/app/lib/dal/sqliteDAL/index/CIndexDAL.js new file mode 100644 index 0000000000000000000000000000000000000000..d90d793b6975242c3938aa21bea344aacaa8f330 --- /dev/null +++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.js @@ -0,0 +1,123 @@ +/** + * Created by cgeek on 22/08/15. + */ + +const co = require('co'); +const constants = require('./../../../constants'); +const indexer = require('./../../../dup/indexer'); +const AbstractSQLite = require('./../AbstractSQLite'); + +module.exports = CIndexDAL; + +function CIndexDAL(driver) { + + "use strict"; + + AbstractSQLite.call(this, driver); + + const that = this; + + this.table = 'c_index'; + this.fields = [ + 'op', + 'issuer', + 'receiver', + 'created_on', + 'written_on', + 'sig', + 'expires_on', + 'expired_on', + 'chainable_on', + 'from_wid', + 'to_wid' + ]; + this.arrays = []; + this.bigintegers = []; + this.booleans = []; + this.pkFields = ['op', 'issuer', 'receiver', 'written_on']; + this.translated = {}; + + this.init = () => co(function *() { + return that.exec('BEGIN;' + + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + + 'op VARCHAR(10) NOT NULL,' + + 'issuer VARCHAR(50) NOT NULL,' + + 'receiver VARCHAR(50) NOT NULL,' + + 'created_on VARCHAR(80) NOT NULL,' + + 'written_on VARCHAR(80) NOT NULL,' + + 'sig VARCHAR(100) NULL,' + + 'expires_on INTEGER NULL,' + + 'expired_on INTEGER NULL,' + + 'chainable_on INTEGER NULL,' + + 'from_wid INTEGER NULL,' + + 'to_wid INTEGER NULL,' + + 'PRIMARY KEY (op,issuer,receiver,written_on)' + + ');' + + 'CREATE INDEX IF NOT EXISTS idx_cindex_issuer ON c_index (issuer);' + + 'CREATE INDEX IF NOT EXISTS idx_cindex_receiver ON c_index (receiver);' + + 'CREATE INDEX IF NOT EXISTS idx_cindex_chainable_on ON c_index (chainable_on);' + + 'COMMIT;', []); + }); + + this.reducablesFrom = (from) => co(function*() { + const reducables = yield that.query('SELECT * FROM ' + that.table + ' WHERE issuer = ? ORDER BY CAST(written_on as integer) ASC', [from]); + return indexer.DUP_HELPERS.reduceBy(reducables, ['issuer', 'receiver', 'created_on']); + }); + + this.trimExpiredCerts = () => co(function*() { + const toDelete = yield that.sqlFind({ expired_on: { $gt: 0 }}); + for (const row of toDelete) { + yield that.exec("DELETE FROM " + that.table + " " + + "WHERE issuer like '" + row.issuer + "' " + + "AND receiver = '" + row.receiver + "' " + + "AND created_on like '" + row.created_on + "'"); + } + }); + + this.getWrittenOn = (blockstamp) => that.sqlFind({ written_on: blockstamp }); + + this.findExpired = (medianTime) => that.query('SELECT * FROM ' + that.table + ' c1 WHERE expires_on <= ? ' + + 'AND NOT EXISTS (' + + ' SELECT * FROM c_index c2' + + ' WHERE c1.issuer = c2.issuer' + + ' AND c1.receiver = c2.receiver' + + ' AND c1.created_on = c2.created_on' + + ' AND c2.op = ?' + + ')', [medianTime, constants.IDX_UPDATE]); + + this.getValidLinksTo = (receiver) => that.query('SELECT * FROM ' + that.table + ' c1 ' + + 'WHERE c1.receiver = ? ' + + 'AND NOT EXISTS (' + + ' SELECT * FROM c_index c2' + + ' WHERE c1.issuer = c2.issuer' + + ' AND c1.receiver = c2.receiver' + + ' AND c1.created_on = c2.created_on' + + ' AND c2.op = ?' + + ')', [receiver, constants.IDX_UPDATE]); + + this.getValidLinksFrom = (issuer) => that.query('SELECT * FROM ' + that.table + ' c1 ' + + 'WHERE c1.issuer = ? ' + + 'AND NOT EXISTS (' + + ' SELECT * FROM c_index c2' + + ' WHERE c1.issuer = c2.issuer' + + ' AND c1.receiver = c2.receiver' + + ' AND c1.created_on = c2.created_on' + + ' AND c2.op = ?' + + ')', [issuer, constants.IDX_UPDATE]); + + this.existsNonReplayableLink = (issuer, receiver) => co(function*() { + const results = that.query('SELECT * FROM ' + that.table + ' c1 ' + + 'WHERE c1.issuer = ? ' + + 'AND c1.receiver = ? ' + + 'AND NOT EXISTS (' + + ' SELECT * FROM c_index c2' + + ' WHERE c1.issuer = c2.issuer' + + ' AND c1.receiver = c2.receiver' + + ' AND c1.created_on = c2.created_on' + + ' AND c2.op = ?' + + ')', [issuer, receiver, constants.IDX_UPDATE]); + return results.length > 0; + }); + + this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\''); +} diff --git a/app/lib/dal/sqliteDAL/index/IIndexDAL.js b/app/lib/dal/sqliteDAL/index/IIndexDAL.js new file mode 100644 index 0000000000000000000000000000000000000000..36620d906a3cb889e726b948b46b551513cc9a0a --- /dev/null +++ b/app/lib/dal/sqliteDAL/index/IIndexDAL.js @@ -0,0 +1,132 @@ +/** + * Created by cgeek on 22/08/15. + */ + +const co = require('co'); +const _ = require('underscore'); +const indexer = require('./../../../dup/indexer'); +const AbstractSQLite = require('./../AbstractSQLite'); + +module.exports = IIndexDAL; + +function IIndexDAL(driver) { + + "use strict"; + + AbstractSQLite.call(this, driver); + + const that = this; + + this.table = 'i_index'; + this.fields = [ + 'op', + 'uid', + 'pub', + 'hash', + 'sig', + 'created_on', + 'written_on', + 'member', + 'wasMember', + 'kick', + 'wotb_id' + ]; + this.arrays = []; + this.bigintegers = []; + this.booleans = ['member', 'wasMember', 'kick']; + this.pkFields = ['op', 'pub', 'created_on', 'written_on']; + this.translated = {}; + + this.init = () => co(function *() { + return that.exec('BEGIN;' + + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + + 'op VARCHAR(10) NOT NULL,' + + 'uid VARCHAR(100) NULL,' + + 'pub VARCHAR(50) NOT NULL,' + + 'hash VARCHAR(80) NULL,' + + 'sig VARCHAR(80) NULL,' + + 'created_on VARCHAR(80) NULL,' + + 'written_on VARCHAR(80) NOT NULL,' + + 'member BOOLEAN NULL,' + + 'wasMember BOOLEAN NULL,' + + 'kick BOOLEAN NULL,' + + 'wotb_id INTEGER NULL,' + + 'PRIMARY KEY (op,pub,created_on,written_on)' + + ');' + + 'CREATE INDEX IF NOT EXISTS idx_iindex_pub ON i_index (pub);' + + 'COMMIT;', []); + }); + + this.getMembers = () => co(function*() { + // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey. + const pubkeys = yield that.query('SELECT DISTINCT(pub) FROM ' + that.table); + // We get the full representation for each member + const reduced = yield pubkeys.map((entry) => co(function*() { + const reducable = yield that.reducable(entry.pub); + return indexer.DUP_HELPERS.reduce(reducable); + })); + // Filter on those to be kicked, return their pubkey + const filtered = _.filter(reduced, (entry) => entry.member); + return filtered.map(toCorrectEntity); + }); + + this.getLatestMember = () => co(function*() { + const max_wotb_id = (yield that.query('SELECT MAX(wotb_id) as max_wotb_id FROM ' + that.table))[0].max_wotb_id; + return entityOrNull('wotb_id', max_wotb_id); + }); + + this.getToBeKickedPubkeys = () => co(function*() { + // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey. + const reducables = indexer.DUP_HELPERS.reduceBy(yield that.sqlFind({ kick: true }), ['pub']); + // We get the full representation for each member + const reduced = yield reducables.map((entry) => co(function*() { + const reducable = yield that.reducable(entry.pub); + return indexer.DUP_HELPERS.reduce(reducable); + })); + // Filter on those to be kicked, return their pubkey + return _.filter(reduced, (entry) => entry.kick).map((entry) => entry.pub); + }); + + this.searchThoseMatching = (search) => co(function*() { + const reducables = indexer.DUP_HELPERS.reduceBy(yield that.sqlFindLikeAny({ + pub: "%" + search + "%", + uid: "%" + search + "%" + }), ['pub']); + // We get the full representation for each member + return yield reducables.map((entry) => co(function*() { + return toCorrectEntity(indexer.DUP_HELPERS.reduce(yield that.reducable(entry.pub))); + })); + }); + + this.getFromPubkey = (pubkey) => entityOrNull('pub', pubkey); + + this.getFromUID = (uid) => entityOrNull('uid', uid); + + this.getFromHash = (hash) => entityOrNull('hash', hash, 'pub'); + + this.reducable = (pub) => this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]); + + this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\''); + + function entityOrNull(field, value, retrieveOnField) { + return co(function*() { + let reducable = yield that.query('SELECT * FROM ' + that.table + ' WHERE ' + field + ' = ?', [value]); + if (reducable.length) { + if (retrieveOnField) { + // Force full retrieval on `pub` field + reducable = yield that.query('SELECT * FROM ' + that.table + ' WHERE pub = ? ORDER BY CAST(written_on as int) ASC', [reducable[0].pub]); + } + return toCorrectEntity(indexer.DUP_HELPERS.reduce(reducable)); + } + return null; + }); + } + + function toCorrectEntity(row) { + // Old field + row.pubkey = row.pub; + row.buid = row.created_on; + row.revocation_sig = null; + return row; + } +} diff --git a/app/lib/dal/sqliteDAL/index/MIndexDAL.js b/app/lib/dal/sqliteDAL/index/MIndexDAL.js new file mode 100644 index 0000000000000000000000000000000000000000..67579f8a89edecd9ab3f5d154ae8159535065100 --- /dev/null +++ b/app/lib/dal/sqliteDAL/index/MIndexDAL.js @@ -0,0 +1,68 @@ +/** + * Created by cgeek on 22/08/15. + */ + +const co = require('co'); +const indexer = require('./../../../dup/indexer'); +const AbstractSQLite = require('./../AbstractSQLite'); + +module.exports = MIndexDAL; + +function MIndexDAL(driver) { + + "use strict"; + + AbstractSQLite.call(this, driver); + + const that = this; + + this.table = 'm_index'; + this.fields = [ + 'op', + 'pub', + 'created_on', + 'written_on', + 'expires_on', + 'expired_on', + 'revokes_on', + 'revoked_on', + 'leaving', + 'revocation' + ]; + this.arrays = []; + this.bigintegers = []; + this.booleans = ['leaving']; + this.pkFields = ['op', 'pub', 'created_on', 'written_on']; + this.translated = {}; + + this.init = () => co(function *() { + return that.exec('BEGIN;' + + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + + 'op VARCHAR(10) NOT NULL,' + + 'pub VARCHAR(50) NOT NULL,' + + 'created_on VARCHAR(80) NOT NULL,' + + 'written_on VARCHAR(80) NOT NULL,' + + 'expires_on INTEGER NULL,' + + 'expired_on INTEGER NULL,' + + 'revokes_on INTEGER NULL,' + + 'revoked_on INTEGER NULL,' + + 'leaving BOOLEAN NULL,' + + 'revocation VARCHAR(80) NULL,' + + 'PRIMARY KEY (op,pub,created_on,written_on)' + + ');' + + 'CREATE INDEX IF NOT EXISTS idx_mindex_pub ON m_index (pub);' + + 'COMMIT;', []); + }); + + this.getReducedMS = (pub) => co(function*() { + const reducables = yield that.reducable(pub); + if (reducables.length) { + return indexer.DUP_HELPERS.reduce(reducables); + } + return null; + }); + + this.reducable = (pub) => this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]); + + this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\''); +} diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.js b/app/lib/dal/sqliteDAL/index/SIndexDAL.js new file mode 100644 index 0000000000000000000000000000000000000000..3f28d6ba019cb7b2390d85ac835e8ac229065559 --- /dev/null +++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.js @@ -0,0 +1,99 @@ +/** + * Created by cgeek on 22/08/15. + */ + +const _ = require('underscore'); +const co = require('co'); +const indexer = require('../../../dup/indexer'); +const AbstractSQLite = require('./../AbstractSQLite'); + +module.exports = SIndexDAL; + +function SIndexDAL(driver) { + + "use strict"; + + AbstractSQLite.call(this, driver); + + const that = this; + + this.table = 's_index'; + this.fields = [ + 'op', + 'tx', + 'identifier', + 'pos', + 'created_on', + 'written_on', + 'written_time', + 'amount', + 'base', + 'locktime', + 'consumed', + 'conditions' + ]; + this.arrays = []; + this.bigintegers = ['amount']; + this.booleans = ['consumed']; + this.pkFields = ['op', 'identifier', 'pos', 'written_on']; + this.translated = {}; + + this.init = () => co(function *() { + return that.exec('BEGIN;' + + 'CREATE TABLE IF NOT EXISTS ' + that.table + ' (' + + 'op VARCHAR(10) NOT NULL,' + + 'tx VARCHAR(80) NULL,' + + 'identifier VARCHAR(64) NOT NULL,' + + 'pos INTEGER NOT NULL,' + + 'created_on VARCHAR(80) NULL,' + + 'written_on VARCHAR(80) NOT NULL,' + + 'written_time INTEGER NOT NULL,' + + 'amount VARCHAR(50) NULL,' + + 'base INTEGER NULL,' + + 'locktime INTEGER NULL,' + + 'consumed BOOLEAN NOT NULL,' + + 'conditions TEXT,' + + 'PRIMARY KEY (op,identifier,pos,written_on)' + + ');' + + 'CREATE INDEX IF NOT EXISTS idx_sindex_identifier ON s_index (identifier);' + + 'CREATE INDEX IF NOT EXISTS idx_sindex_pos ON s_index (pos);' + + 'COMMIT;', []); + }); + + this.removeBlock = (blockstamp) => that.exec('DELETE FROM ' + that.table + ' WHERE written_on = \'' + blockstamp + '\''); + + this.getSource = (identifier, pos) => co(function*() { + const reducable = yield that.sqlFind({ identifier, pos }); + if (reducable.length == 0) { + return null; + } else { + const src = indexer.DUP_HELPERS.reduce(reducable); + src.type = src.tx ? 'T' : 'D'; + return src; + } + }); + + this.getUDSources = (pubkey) => co(function*() { + const reducables = yield that.sqlFind({ + conditions: { $contains: pubkey }, + tx: { $null: true } + }); + const reduced = indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos']).map((src) => { + src.type = src.tx ? 'T' : 'D'; + return src; + }); + return _.sortBy(reduced, (row) => row.type == 'D' ? 0 : 1); + }); + + this.getAvailableForPubkey = (pubkey) => co(function*() { + const reducables = yield that.sqlFind({ + conditions: { $contains: 'SIG(' + pubkey + ')' } + }); + const sources = indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos']).map((src) => { + src.type = src.tx ? 'T' : 'D'; + return src; + }); + const filtered = _.filter(sources, (src) => !src.consumed); + return _.sortBy(filtered, (row) => row.type == 'D' ? 0 : 1); + }); +} diff --git a/app/lib/dup/indexer.js b/app/lib/dup/indexer.js new file mode 100644 index 0000000000000000000000000000000000000000..1cd4d0ab8140db2213553b78baddf050c247aaf6 --- /dev/null +++ b/app/lib/dup/indexer.js @@ -0,0 +1,1785 @@ +"use strict"; + +const co = require('co'); +const _ = require('underscore'); +const constants = require('../constants'); +const rawer = require('../ucp/rawer'); +const unlock = require('../ucp/txunlock'); +const keyring = require('../crypto/keyring'); +const Block = require('../entity/block'); +const Identity = require('../entity/identity'); +const Certification = require('../entity/certification'); +const Membership = require('../entity/membership'); +const Transaction = require('../entity/transaction'); + +const indexer = module.exports = { + + localIndex: (block, conf) => { + + /******************** + * GENERAL BEHAVIOR + * + * * for each newcomer: 2 indexes (1 iindex CREATE, 1 mindex CREATE) + * * for each comeback: 2 indexes (1 iindex UPDATE, 1 mindex UPDATE) + * * for each renewer: 1 index ( 1 mindex UPDATE) + * * for each leaver: 1 index ( 1 mindex UPDATE) + * * for each revoked: 1 index ( 1 mindex UPDATE) + * * for each excluded: 1 indexes (1 iindex UPDATE) + * + * * for each certification: 1 index (1 cindex CREATE) + * + * * for each tx output: 1 index (1 sindex CREATE) + * * for each tx input: 1 index (1 sindex UPDATE) + */ + + const index = []; + + /*************************** + * IDENTITIES INDEX (IINDEX) + **************************/ + for (const identity of block.identities) { + const idty = Identity.statics.fromInline(identity); + // Computes the hash if not done yet + index.push({ + index: constants.I_INDEX, + op: constants.IDX_CREATE, + uid: idty.uid, + pub: idty.pubkey, + hash: idty.hash, + sig: idty.sig, + created_on: idty.buid, + written_on: [block.number, block.hash].join('-'), + member: true, + wasMember: true, + kick: false, + wid: null // wotb id + }); + } + + /**************************** + * MEMBERSHIPS INDEX (MINDEX) + ***************************/ + // Joiners (newcomer or join back) + for (const inlineMS of block.joiners) { + const ms = Membership.statics.fromInline(inlineMS); + const matchesANewcomer = _.filter(index, (row) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0; + if (matchesANewcomer) { + // Newcomer + index.push({ + index: constants.M_INDEX, + op: constants.IDX_CREATE, + pub: ms.issuer, + created_on: [ms.number, ms.fpr].join('-'), + written_on: [block.number, block.hash].join('-'), + type: 'JOIN', + expires_on: conf.msValidity, + revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, + revoked_on: null, + leaving: false + }); + } else { + // Join back + index.push({ + index: constants.M_INDEX, + op: constants.IDX_UPDATE, + pub: ms.issuer, + created_on: [ms.number, ms.fpr].join('-'), + written_on: [block.number, block.hash].join('-'), + type: 'JOIN', + expires_on: conf.msValidity, + revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, + revoked_on: null, + leaving: null + }); + index.push({ + index: constants.I_INDEX, + op: constants.IDX_UPDATE, + uid: null, + pub: ms.issuer, + created_on: null, + written_on: [block.number, block.hash].join('-'), + member: true, + wasMember: null, + kick: null, + wid: null + }); + } + } + // Actives + for (const inlineMS of block.actives) { + const ms = Membership.statics.fromInline(inlineMS); + // Renew + index.push({ + index: constants.M_INDEX, + op: constants.IDX_UPDATE, + pub: ms.issuer, + created_on: [ms.number, ms.fpr].join('-'), + written_on: [block.number, block.hash].join('-'), + type: 'ACTIVE', + expires_on: conf.msValidity, + revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, + revoked_on: null, + leaving: null + }); + } + // Leavers + for (const inlineMS of block.leavers) { + const ms = Membership.statics.fromInline(inlineMS); + index.push({ + index: constants.M_INDEX, + op: constants.IDX_UPDATE, + pub: ms.issuer, + created_on: [ms.number, ms.fpr].join('-'), + written_on: [block.number, block.hash].join('-'), + type: 'LEAVE', + expires_on: null, + revokes_on: null, + revoked_on: null, + leaving: true + }); + } + // Revoked + for (const inlineRevocation of block.revoked) { + const revocation = Identity.statics.revocationFromInline(inlineRevocation); + index.push({ + index: constants.M_INDEX, + op: constants.IDX_UPDATE, + pub: revocation.pubkey, + created_on: [block.number, block.hash].join('-'), + written_on: [block.number, block.hash].join('-'), + expires_on: null, + revokes_on: null, + revoked_on: [block.number, block.hash].join('-'), + revocation: revocation.sig, + leaving: false + }); + } + // Excluded + for (const excluded of block.excluded) { + index.push({ + index: constants.I_INDEX, + op: constants.IDX_UPDATE, + uid: null, + pub: excluded, + created_on: null, + written_on: [block.number, block.hash].join('-'), + member: false, + wasMember: null, + kick: false, + wid: null + }); + } + + /******************************* + * CERTIFICATIONS INDEX (CINDEX) + ******************************/ + for (const inlineCert of block.certifications) { + const cert = Certification.statics.fromInline(inlineCert); + index.push({ + index: constants.C_INDEX, + op: constants.IDX_CREATE, + issuer: cert.pubkey, + receiver: cert.to, + created_on: cert.block_number, + written_on: [block.number, block.hash].join('-'), + sig: cert.sig, + chainable_on: parseInt(block.medianTime) + conf.sigPeriod, + expires_on: conf.sigValidity, + expired_on: 0, + from_wid: null, + to_wid: null + }); + } + + return index.concat(module.exports.localSIndex(block)); + }, + + localSIndex: (block) => { + /******************************* + * SOURCES INDEX (SINDEX) + ******************************/ + const index = []; + if (!block.transactions && block.getTransactions) { + const txs = block.getTransactions(); + block.transactions = []; + for (const tx of txs) { + block.transactions.push({ + version: tx.version, + comment: tx.comment, + issuers: tx.issuers, + signatures: tx.signatures, + inputs: tx.inputs.map((i) => i.raw), + outputs: tx.outputs.map((o) => o.raw) + }); + } + } + for (const obj of block.transactions) { + obj.currency = block.currency; + obj.issuers = obj.signatories; + const tx = new Transaction(obj); + const txObj = tx.getTransaction(); + const txHash = tx.getHash(true); + let k = 0; + for (const input of txObj.inputs) { + index.push({ + index: constants.S_INDEX, + op: constants.IDX_UPDATE, + tx: txHash, + identifier: input.identifier, + pos: input.pos, + created_on: tx.blockstamp, + written_on: [block.number, block.hash].join('-'), + written_time: block.medianTime, + locktime: obj.locktime, + unlock: txObj.unlocks[k], + amount: input.amount, + base: input.base, + consumed: true, + conditions: null, + txObj: txObj + }); + k++; + } + + let i = 0; + for (const output of txObj.outputs) { + index.push({ + index: constants.S_INDEX, + op: constants.IDX_CREATE, + tx: txHash, + identifier: txHash, + pos: i++, + written_on: [block.number, block.hash].join('-'), + written_time: block.medianTime, + locktime: obj.locktime, + amount: output.amount, + base: output.base, + consumed: false, + conditions: output.conditions, + txObj: obj + }); + } + } + return index; + }, + + quickCompleteGlobalScope: (block, conf, bindex, iindex, mindex, cindex, sindex, dal) => co(function*() { + + function range(start, end, property) { + return co(function*() { + let range; + end = Math.min(end, bindex.length); + if (start == 1) { + range = bindex.slice(-end); + } else { + range = bindex.slice(-end, -start + 1); + } + range.reverse(); + if (property) { + // Filter on a particular property + return range.map((b) => b[property]); + } else { + return range; + } + }); + } + + function head(n) { + return co(function*() { + return (yield range(n, n))[0]; + }); + } + + const HEAD = { + version: block.version, + bsize: Block.statics.getLen(block), + hash: Block.statics.getHash(block), + issuer: block.issuer, + time: block.time, + medianTime: block.medianTime, + number: block.number, + powMin: block.powMin, + unitBase: block.unitbase, + membersCount: block.membersCount, + dividend: block.dividend + }; + const HEAD_1 = yield head(1); + + if (HEAD.number == 0) { + HEAD.dividend = conf.ud0; + } + else if (!HEAD.dividend) { + HEAD.dividend = HEAD_1.dividend; + } else { + HEAD.new_dividend = HEAD.dividend; + } + + // BR_G04 + yield indexer.prepareIssuersCount(HEAD, range, HEAD_1); + + // BR_G05 + indexer.prepareIssuersFrame(HEAD, HEAD_1); + + // BR_G06 + indexer.prepareIssuersFrameVar(HEAD, HEAD_1); + + // BR_G07 + yield indexer.prepareAvgBlockSize(HEAD, range); + + // BR_G09 + indexer.prepareDiffNumber(HEAD, HEAD_1, conf); + + // BR_G11 + indexer.prepareUDTime(HEAD, HEAD_1, conf); + + // BR_G15 + indexer.prepareMass(HEAD, HEAD_1); + + // BR_G16 + yield indexer.prepareSpeed(HEAD, head, conf); + + // BR_G19 + yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); + + // BR_G22 + yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); + + // BR_G37 + yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal); + + // BR_G104 + yield indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, dal); + + // BR_G105 + yield indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, dal); + + return HEAD; + }), + + completeGlobalScope: (block, conf, index, dal) => co(function*() { + + const iindex = module.exports.iindex(index); + const mindex = module.exports.mindex(index); + const cindex = module.exports.cindex(index); + const sindex = module.exports.sindex(index); + + const range = dal.range; + const head = dal.head; + + const HEAD = { + version: block.version, + bsize: Block.statics.getLen(block), + hash: Block.statics.getHash(block), + issuer: block.issuer, + time: block.time, + powMin: block.powMin + }; + const HEAD_1 = yield head(1); + if (HEAD_1) { + HEAD_1.currency = conf.currency; + } + + // BR_G01 + indexer.prepareNumber(HEAD, HEAD_1); + + // BR_G02 + if (HEAD.number > 0) { + HEAD.previousHash = HEAD_1.hash; + } else { + HEAD.previousHash = null; + } + + // BR_G99 + if (HEAD.number > 0) { + HEAD.currency = HEAD_1.currency; + } else { + HEAD.currency = null; + } + + // BR_G03 + if (HEAD.number > 0) { + HEAD.previousIssuer = HEAD_1.issuer; + } else { + HEAD.previousIssuer = null; + } + + // BR_G03 + if (HEAD.number > 0) { + HEAD.issuerIsMember = reduce(yield dal.iindexDAL.reducable(HEAD.issuer)).member; + } else { + HEAD.issuerIsMember = reduce(_.where(iindex, { pub: HEAD.issuer })).member; + } + + // BR_G04 + yield indexer.prepareIssuersCount(HEAD, range, HEAD_1); + + // BR_G05 + indexer.prepareIssuersFrame(HEAD, HEAD_1); + + // BR_G06 + indexer.prepareIssuersFrameVar(HEAD, HEAD_1); + + // BR_G07 + yield indexer.prepareAvgBlockSize(HEAD, range); + + // BR_G08 + if (HEAD.number > 0) { + HEAD.medianTime = average(yield range(1, Math.min(conf.medianTimeBlocks, HEAD.number), 'time')); // TODO: median + } else { + HEAD.medianTime = HEAD.time; + } + + // BR_G09 + indexer.prepareDiffNumber(HEAD, HEAD_1, conf); + + // BR_G10 + if (HEAD.number == 0) { + HEAD.membersCount = count(_.filter(iindex, (entry) => entry.member === true)); + } else { + HEAD.membersCount = HEAD_1.membersCount + + count(_.filter(iindex, (entry) => entry.member === true)) + - count(_.filter(iindex, (entry) => entry.member === false)); + } + + // BR_G11 + indexer.prepareUDTime(HEAD, HEAD_1, conf); + + // BR_G12 + if (HEAD.number == 0) { + HEAD.unitBase = 0; + } else { + HEAD.unitBase = HEAD_1.unitBase; + } + + // BR_G13 + indexer.prepareDividend(HEAD, HEAD_1, conf); + + // BR_G14 + indexer.prepareUnitBase(HEAD, HEAD_1, conf); + + // BR_G15 + indexer.prepareMass(HEAD, HEAD_1); + + // BR_G16 + yield indexer.prepareSpeed(HEAD, head, conf); + + // BR_G17 + if (HEAD.number > 0) { + + const ratio = HEAD.version > 3 ? 1.189 : Math.sqrt(1.066); + const maxGenTime = Math.ceil(conf.avgGenTime * ratio); + const minGenTime = Math.floor(conf.avgGenTime / ratio); + const minSpeed = 1/ maxGenTime; + const maxSpeed = 1/ minGenTime; + + if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed >= maxSpeed && (HEAD_1.powMin + 2) % 16 == 0) { + HEAD.powMin = HEAD_1.powMin + 2; + } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed >= maxSpeed) { + HEAD.powMin = HEAD_1.powMin + 1; + } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed <= minSpeed && HEAD_1.powMin % 16 == 0) { + HEAD.powMin = Math.max(0, HEAD_1.powMin - 2); + } else if (HEAD.diffNumber != HEAD_1.diffNumber && HEAD.speed <= minSpeed) { + HEAD.powMin = Math.max(0, HEAD_1.powMin - 1); + } else { + HEAD.powMin = HEAD_1.powMin; + } + } + + // BR_G18 + yield indexer.preparePersonalizedPoW(HEAD, HEAD_1, range, conf); + + // BR_G19 + yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); + + // BR_G20 + yield iindex.map((ENTRY) => co(function*() { + if (ENTRY.op == constants.IDX_CREATE) { + ENTRY.uidUnique = count(yield dal.iindexDAL.sqlFind({ uid: ENTRY.uid })) == 0; + } else { + ENTRY.uidUnique = true; + } + })); + + // BR_G21 + yield iindex.map((ENTRY) => co(function*() { + if (ENTRY.op == constants.IDX_CREATE) { + ENTRY.pubUnique = count(yield dal.iindexDAL.sqlFind({pub: ENTRY.pub})) == 0; + } else { + ENTRY.pubUnique = true; + } + })); + + // BR_G33 + yield iindex.map((ENTRY) => co(function*() { + if (ENTRY.member !== false) { + ENTRY.excludedIsMember = true; + } else { + ENTRY.excludedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + } + })); + + // BR_G35 + yield iindex.map((ENTRY) => co(function*() { + ENTRY.isBeingKicked = ENTRY.member === false; + })); + + // BR_G36 + yield iindex.map((ENTRY) => co(function*() { + ENTRY.hasToBeExcluded = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).kick; + })); + + // BR_G22 + yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); + + // BR_G23 + yield mindex.map((ENTRY) => co(function*() { + if (!ENTRY.revoked_on) { + const created_on = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).created_on; + if (created_on != null) { + ENTRY.numberFollowing = number(ENTRY.created_on) > number(created_on); + } else { + ENTRY.numberFollowing = true; // Follows nothing + } + } else { + ENTRY.numberFollowing = true; + } + })); + + // BR_G24 + // Global testing, because of wotb + const oneIsOutdistanced = yield checkPeopleAreNotOudistanced( + HEAD.version, + _.filter(mindex, (entry) => !entry.revoked_on).map((entry) => entry.pub), + cindex.reduce((newLinks, c) => { + newLinks[c.receiver] = newLinks[c.receiver] || []; + newLinks[c.receiver].push(c.issuer); + return newLinks; + }, {}), + // Newcomers + _.where(iindex, { op: constants.IDX_CREATE }).map((entry) => entry.pub), + conf, + dal + ); + mindex.map((ENTRY) => { + if (ENTRY.expires_on) { + ENTRY.distanceOK = !oneIsOutdistanced; + } else { + ENTRY.distanceOK = true; + } + }); + + // BR_G25 + yield mindex.map((ENTRY) => co(function*() { + ENTRY.onRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on != null; + })); + + // BR_G26 + yield _.filter(mindex, (entry) => entry.op == constants.IDX_UPDATE && entry.expired_on === 0).map((ENTRY) => co(function*() { + ENTRY.joinsTwice = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member == true; + })); + + // BR_G27 + yield mindex.map((ENTRY) => co(function*() { + if (ENTRY.type == 'JOIN' || ENTRY.type == 'ACTIVE') { + const existing = count(yield dal.cindexDAL.sqlFind({ receiver: ENTRY.pub, expired_on: 0 })); + const pending = count(_.filter(cindex, (c) => c.receiver == ENTRY.pub && c.expired_on == 0)); + ENTRY.enoughCerts = (existing + pending) >= conf.sigQty; + } else { + ENTRY.enoughCerts = true; + } + })); + + // BR_G28 + yield mindex.map((ENTRY) => co(function*() { + if (ENTRY.type == 'LEAVE') { + ENTRY.leaverIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + } else { + ENTRY.leaverIsMember = true; + } + })); + + // BR_G29 + yield mindex.map((ENTRY) => co(function*() { + if (ENTRY.type == 'ACTIVE') { + const reducable = yield dal.iindexDAL.reducable(ENTRY.pub); + ENTRY.activeIsMember = reduce(reducable).member; + } else { + ENTRY.activeIsMember = true; + } + })); + + // BR_G30 + yield mindex.map((ENTRY) => co(function*() { + if (!ENTRY.revoked_on) { + ENTRY.revokedIsMember = true; + } else { + ENTRY.revokedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + } + })); + + // BR_G31 + yield mindex.map((ENTRY) => co(function*() { + if (!ENTRY.revoked_on) { + ENTRY.alreadyRevoked = false; + } else { + ENTRY.alreadyRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on; + } + })); + + // BR_G32 + yield mindex.map((ENTRY) => co(function*() { + if (!ENTRY.revoked_on) { + ENTRY.revocationSigOK = true; + } else { + ENTRY.revocationSigOK = yield sigCheckRevoke(ENTRY, dal, block.currency); + } + })); + + // BR_G34 + yield mindex.map((ENTRY) => co(function*() { + ENTRY.isBeingRevoked = ENTRY.revoked; + })); + + // BR_G37 + yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal); + + // BR_G38 + if (HEAD.number > 0) { + yield cindex.map((ENTRY) => co(function*() { + const rows = yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, chainable_on: { $gt: HEAD_1.medianTime }}); + ENTRY.unchainables = count(rows); + })); + } + + // BR_G39 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.stock = count(yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, expired_on: 0 })); + })); + + // BR_G40 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.fromMember = reduce(yield dal.iindexDAL.reducable(ENTRY.issuer)).member; + })); + + // BR_G41 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.toMember = reduce(yield dal.iindexDAL.reducable(ENTRY.receiver)).member; + })); + + // BR_G42 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.toNewcomer = count(_.where(iindex, { member: true, pub: ENTRY.receiver })) > 0; + })); + + // BR_G43 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.toLeaver = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).leaving; + })); + + // BR_G44 + yield cindex.map((ENTRY) => co(function*() { + const reducable = yield dal.cindexDAL.sqlFind({ issuer: ENTRY.issuer, receiver: ENTRY.receiver }); + ENTRY.isReplay = count(reducable) > 0 && reduce(reducable).expired_on === 0; + })); + + // BR_G45 + yield cindex.map((ENTRY) => co(function*() { + ENTRY.sigOK = checkCertificationIsValid(block, ENTRY, (pub) => { + let localInlineIdty = block.getInlineIdentity(pub); + if (localInlineIdty) { + return Identity.statics.fromInline(localInlineIdty); + } + return dal.getWrittenIdtyByPubkey(pub); + }, conf, dal); + })); + + // BR_G102 + yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { + if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { + ENTRY.age = 0; + } else { + if (HEAD.version >= 3) { + let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) { + ENTRY.age = HEAD_1.medianTime - ref.medianTime; + } else { + ENTRY.age = constants.TRANSACTION_EXPIRY_DELAY + 1; + } + } else { + ENTRY.age = 0; + } + } + })); + + // BR_G46 + yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { + if (HEAD.version > 2) { + const reducable = yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos, + amount: ENTRY.amount, + base: ENTRY.base + }); + // TODO: not in v1.0 + let available; + if (count(reducable) > 0) { + available = reduce(reducable).consumed === false; + } else { + const src = yield dal.getSource(ENTRY.identifier, ENTRY.pos); + available = src.consumed === false; + } + ENTRY.available = available; + } else { + ENTRY.available = reduce(yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos + })).consumed === false; + } + })); + + // BR_G47 + yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { + if (HEAD.version > 2) { + let source = _.filter(sindex, (src) => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos && src.conditions)[0]; + if (!source) { + const reducable = yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos, + amount: ENTRY.amount, + base: ENTRY.base + }); + // TODO: not in v1.0 + if (count(reducable) > 0) { + source = reduce(reducable); + } else { + source = yield dal.getSource(ENTRY.identifier, ENTRY.pos); + } + } + ENTRY.conditions = source.conditions; + ENTRY.isLocked = !txSourceUnlock(ENTRY, source); + } else { + const source = reduce(yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos + })); + ENTRY.conditions = source.conditions; + ENTRY.isLocked = !txSourceUnlock(ENTRY, source); + } + })); + + // BR_G48 + yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { + if (HEAD.version > 2) { + ENTRY.isTimeLocked = ENTRY.written_time - reduce(yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos, + amount: ENTRY.amount, + base: ENTRY.base + })).written_time < ENTRY.locktime; + } else { + ENTRY.isTimeLocked = ENTRY.written_time - reduce(yield dal.sindexDAL.sqlFind({ + identifier: ENTRY.identifier, + pos: ENTRY.pos + })).written_time < ENTRY.locktime; + } + })); + + return HEAD; + }), + + // BR_G01 + prepareNumber: (HEAD, HEAD_1) => { + if (HEAD_1) { + HEAD.number = HEAD_1.number + 1; + } else { + HEAD.number = 0; + } + }, + + // BR_G04 + prepareIssuersCount: (HEAD, range, HEAD_1) => co(function*() { + if (HEAD.number == 0) { + HEAD.issuersCount = 0; + } else if(HEAD_1.version > 2) { + HEAD.issuersCount = count(uniq(yield range(1, HEAD_1.issuersFrame, 'issuer'))); + } else { + HEAD.issuersCount = count(uniq(yield range(1, 40, 'issuer'))); + } + }), + + // BR_G05 + prepareIssuersFrame: (HEAD, HEAD_1) => { + if (HEAD.number == 0) { + HEAD.issuersFrame = 1; + } else if (HEAD_1.version == 2) { + HEAD.issuersFrame = 40 + } else if (HEAD_1.issuersFrameVar > 0) { + HEAD.issuersFrame = HEAD_1.issuersFrame + 1 + } else if (HEAD_1.issuersFrameVar < 0) { + HEAD.issuersFrame = HEAD_1.issuersFrame - 1 + } else { + HEAD.issuersFrame = HEAD_1.issuersFrame + } + }, + + // BR_G06 + prepareIssuersFrameVar: (HEAD, HEAD_1) => { + if (HEAD.number == 0 || HEAD_1.version == 2) { + HEAD.issuersFrameVar = 0; + } else { + // TODO: remove this bug + let issuersVar = (HEAD.issuersCount - HEAD_1.issuersCount); + if (issuersVar > 0) issuersVar = 1; + if (issuersVar < 0) issuersVar = -1; + if (issuersVar == 0) issuersVar = 0; + if (HEAD_1.issuersFrameVar > 0) { + HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar - 1; + } else if (HEAD_1.issuersFrameVar < 0) { + HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar + 1; + } else { + HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5*issuersVar; + } + } + }, + + // BR_G07 + prepareAvgBlockSize: (HEAD, range) => co(function*() { + HEAD.avgBlockSize = average(yield range(1, HEAD.issuersCount, 'bsize')); + }), + + // BR_G09 + prepareDiffNumber: (HEAD, HEAD_1, conf) => { + if (HEAD.number == 0) { + HEAD.diffNumber = HEAD.number + conf.dtDiffEval; + } else if (HEAD_1.diffNumber <= HEAD.number) { + HEAD.diffNumber = HEAD_1.diffNumber + conf.dtDiffEval; + } else { + HEAD.diffNumber = HEAD_1.diffNumber; + } + }, + + // BR_G11 + prepareUDTime: (HEAD, HEAD_1, conf) => { + if (HEAD.number == 0) { + HEAD.udTime = HEAD.medianTime + conf.dt; + } else if (HEAD_1.udTime <= HEAD.medianTime) { + HEAD.udTime = HEAD_1.udTime + conf.dt; + } else { + HEAD.udTime = HEAD_1.udTime; + } + }, + + // BR_G13 + prepareDividend: (HEAD, HEAD_1, conf) => { + if (HEAD.number == 0) { + HEAD.dividend = conf.ud0; + HEAD.new_dividend = null; + } else if (HEAD.udTime != HEAD_1.udTime) { + if (HEAD.version == 2) { + // DUA + const M = HEAD_1.mass; + const N = HEAD.membersCount; + const previousUB = HEAD_1.unitBase; + const previousUD = HEAD_1.dividend; + const c = conf.c; + HEAD.dividend = Math.ceil(Math.max(previousUD, c * M / Math.pow(10, previousUB) / N)); + HEAD.new_dividend = HEAD.dividend; + } + else if (HEAD.version == 3) { + // DUB + const c = conf.c; + HEAD.dividend = parseInt(((1 + c) * HEAD_1.dividend).toFixed(0)); + HEAD.new_dividend = HEAD.dividend; + } + else if (HEAD.version >= 4) { + // DUG + const previousUB = HEAD_1.unitBase; + // TODO: ceiling for v1.0 + HEAD.dividend = parseInt((HEAD_1.dividend + Math.pow(conf.c, 2) * HEAD_1.mass / HEAD.membersCount / Math.pow(10, previousUB)).toFixed(0)); + HEAD.new_dividend = HEAD.dividend; + } + } else { + HEAD.dividend = HEAD_1.dividend; + HEAD.new_dividend = null; + } + }, + + // BR_G14 + prepareUnitBase: (HEAD) => { + if (HEAD.dividend >= Math.pow(10, 6)) { + HEAD.dividend = Math.ceil(HEAD.dividend / 10); + HEAD.new_dividend = HEAD.dividend; + HEAD.unitBase = HEAD.unitBase + 1; + } + }, + + // BR_G15 + prepareMass: (HEAD, HEAD_1) => { + if (HEAD.number == 0) { + HEAD.mass = 0; + } + else if (HEAD.udTime != HEAD_1.udTime) { + HEAD.mass = HEAD_1.mass + HEAD.dividend * Math.pow(10, HEAD.unitBase) * HEAD.membersCount; + } else { + HEAD.mass = HEAD_1.mass; + } + }, + + // BR_G16 + prepareSpeed: (HEAD, head, conf) => co(function*() { + if (HEAD.number == 0) { + HEAD.speed = 0; + } else { + const quantity = Math.min(conf.dtDiffEval, HEAD.number); + const elapsed = (HEAD.medianTime - (yield head(quantity)).medianTime); + if (!elapsed) { + HEAD.speed = 100; + } else { + HEAD.speed = quantity / elapsed; + } + } + }), + + // BR_G18 + preparePersonalizedPoW: (HEAD, HEAD_1, range, conf) => co(function*() { + let nbPersonalBlocksInFrame, medianOfBlocksInFrame, blocksOfIssuer; + let nbPreviousIssuers = 0, nbBlocksSince = 0; + if (HEAD.number == 0) { + nbPersonalBlocksInFrame = 0; + medianOfBlocksInFrame = 1; + } else { + const blocksInFrame = yield range(1, HEAD_1.issuersFrame); + const issuersInFrame = yield range(1, HEAD_1.issuersFrame, 'issuer'); + blocksOfIssuer = _.filter(blocksInFrame, (entry) => entry.issuer == HEAD.issuer); + nbPersonalBlocksInFrame = count(blocksOfIssuer); + const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer) => count(_.where(blocksInFrame, { issuer }))); + medianOfBlocksInFrame = median(blocksPerIssuerInFrame); + if (nbPersonalBlocksInFrame == 0) { + nbPreviousIssuers = 0; + nbBlocksSince = 0; + } else { + const last = blocksOfIssuer[0]; + nbPreviousIssuers = last.issuersCount; + nbBlocksSince = HEAD_1.number - last.number; + } + } + + if (HEAD.version == 2) { + + // V0.2 Hardness + if (nbPersonalBlocksInFrame > 0) { + nbPreviousIssuers--; + } + HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince))); + + } else if (HEAD.version == 3) { + + // V0.3 Hardness + HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince))); + + } else if (HEAD.version == 4) { + + // V0.4 Hardness + const PERSONAL_EXCESS = Math.max(0, (nbPersonalBlocksInFrame / 5) - 1); + const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189)); + HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + PERSONAL_HANDICAP; + + } else if (HEAD.version == 5) { + + // V0.5 Hardness + const PERSONAL_EXCESS = Math.max(0, ((nbPersonalBlocksInFrame + 1)/ medianOfBlocksInFrame) - 1); + const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189)); + HEAD.issuerDiff = HEAD.powMin + PERSONAL_HANDICAP; + + } else if (HEAD.version >= 6) { + + // V0.6 Hardness + const PERSONAL_EXCESS = Math.max(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1); + const PERSONAL_HANDICAP = Math.floor(Math.log(1 + PERSONAL_EXCESS) / Math.log(1.189)); + HEAD.issuerDiff = Math.max(HEAD.powMin, HEAD.powMin * Math.floor(conf.percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + PERSONAL_HANDICAP; + if ((HEAD.issuerDiff + 1) % 16 == 0) { + HEAD.issuerDiff += 1; + } + } + + HEAD.powRemainder = HEAD.issuerDiff % 16; + HEAD.powZeros = (HEAD.issuerDiff - HEAD.powRemainder) / 16; + }), + + // BR_G19 + prepareIdentitiesAge: (iindex, HEAD, HEAD_1, conf, dal) => co(function*() { + yield _.where(iindex, { op: constants.IDX_CREATE }).map((ENTRY) => co(function*() { + if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { + ENTRY.age = 0; + } else { + if (HEAD.version <= 4) { + let ref = yield dal.getBlock(number(ENTRY.created_on)); + if (ref) { // blockstamp was not checked prior to 0.5 + ENTRY.age = HEAD_1.medianTime - ref.medianTime; + } else { + ENTRY.age = conf.idtyWindow + 1; + } + } else { + let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) { + ENTRY.age = HEAD_1.medianTime - ref.medianTime; + } else { + ENTRY.age = conf.idtyWindow + 1; + } + } + } + })); + }), + + // BR_G22 + prepareMembershipsAge: (mindex, HEAD, HEAD_1, conf, dal) => co(function*() { + yield _.filter(mindex, (entry) => !entry.revoked_on).map((ENTRY) => co(function*() { + if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { + ENTRY.age = 0; + } else { + let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + if (ref && blockstamp(ref.number, ref.hash) == ENTRY.created_on) { + ENTRY.age = HEAD_1.medianTime - ref.medianTime; + } else { + ENTRY.age = conf.msWindow + 1; + } + } + })); + }), + + // BR_G37 + prepareCertificationsAge: (cindex, HEAD, HEAD_1, conf, dal) => co(function*() { + yield cindex.map((ENTRY) => co(function*() { + if (HEAD.number == 0) { + ENTRY.age = 0; + } else { + let ref = yield dal.getBlock(number(ENTRY.created_on)); + if (ref) { + ENTRY.age = HEAD_1.medianTime - ref.medianTime; + } else { + ENTRY.age = conf.sigWindow + 1; + } + } + })); + }), + + // BR_G49 + ruleVersion: (HEAD, HEAD_1) => { + if (HEAD.number > 0) { + return HEAD.version == HEAD_1.version || HEAD.version == HEAD_1.version + 1; + } + return true; + }, + + // BR_G50 + ruleBlockSize: (HEAD) => HEAD.bsize < indexer.DUP_HELPERS.getMaxBlockSize(HEAD), + + // BR_G98 + ruleCurrency: (block, HEAD) => { + if (HEAD.number > 0) { + return block.currency === HEAD.currency; + } + return true; + }, + + // BR_G51 + ruleNumber: (block, HEAD) => block.number == HEAD.number, + + // BR_G52 + rulePreviousHash: (block, HEAD) => block.previousHash == HEAD.previousHash || (!block.previousHash && !HEAD.previousHash), + + // BR_G53 + rulePreviousIssuer: (block, HEAD) => block.previousIssuer == HEAD.previousIssuer || (!block.previousIssuer && !HEAD.previousIssuer), + + // BR_G101 + ruleIssuerIsMember: (HEAD) => HEAD.issuerIsMember == true, + + // BR_G54 + ruleIssuersCount: (block, HEAD) => { + if (HEAD.version > 2) { + return block.issuersCount == HEAD.issuersCount; + } + }, + + // BR_G55 + ruleIssuersFrame: (block, HEAD) => { + if (HEAD.version > 2) { + return block.issuersFrame == HEAD.issuersFrame; + } + }, + + // BR_G56 + ruleIssuersFrameVar: (block, HEAD) => { + if (HEAD.version > 2) { + return block.issuersFrameVar == HEAD.issuersFrameVar; + } + }, + + // BR_G57 + ruleMedianTime: (block, HEAD) => block.medianTime == HEAD.medianTime, + + // BR_G58 + ruleDividend: (block, HEAD) => block.dividend == HEAD.new_dividend, + + // BR_G59 + ruleUnitBase: (block, HEAD) => { + if (HEAD.version >= 3) { + return block.unitbase == HEAD.unitBase; + } + }, + + // BR_G60 + ruleMembersCount: (block, HEAD) => block.membersCount == HEAD.membersCount, + + // BR_G61 + rulePowMin: (block, HEAD) => { + if (HEAD.number > 0) { + return block.powMin == HEAD.powMin; + } + }, + + // BR_G62 + ruleProofOfWork: (HEAD) => { + // Compute exactly how much zeros are required for this block's issuer + const remainder = HEAD.powRemainder; + const nbZerosReq = HEAD.powZeros; + const highMark = constants.PROOF_OF_WORK.UPPER_BOUND[remainder]; + const powRegexp = new RegExp('^0{' + nbZerosReq + '}' + '[0-' + highMark + ']'); + try { + if (!HEAD.hash.match(powRegexp)) { + const givenZeros = Math.max(0, Math.min(nbZerosReq, HEAD.hash.match(/^0*/)[0].length)); + const c = HEAD.hash.substr(givenZeros, 1); + throw Error('Wrong proof-of-work level: given ' + givenZeros + ' zeros and \'' + c + '\', required was ' + nbZerosReq + ' zeros and an hexa char between [0-' + highMark + ']'); + } + return true; + } catch (e) { + return false; + } + }, + + // BR_G63 + ruleIdentityWritability: (iindex, conf) => { + for (const ENTRY of iindex) { + if (ENTRY.age > conf.idtyWindow) return false; + } + }, + + // BR_G64 + ruleMembershipWritability: (mindex, conf) => { + for (const ENTRY of mindex) { + if (ENTRY.age > conf.msWindow) return false; + } + }, + + // BR_G65 + ruleCertificationWritability: (cindex, conf) => { + for (const ENTRY of cindex) { + if (ENTRY.age > conf.sigWindow) return false; + } + }, + + // BR_G66 + ruleCertificationStock: (cindex, conf) => { + for (const ENTRY of cindex) { + if (ENTRY.stock > conf.sigStock) return false; + } + }, + + // BR_G67 + ruleCertificationPeriod: (cindex) => { + for (const ENTRY of cindex) { + if (ENTRY.unchainables > 0) return false; + } + }, + + // BR_G68 + ruleCertificationFromMember: (HEAD, cindex) => { + if (HEAD.number > 0) { + for (const ENTRY of cindex) { + if (!ENTRY.fromMember) return false; + } + } + }, + + // BR_G69 + ruleCertificationToMemberOrNewcomer: (cindex) => { + for (const ENTRY of cindex) { + if (!ENTRY.toMember && !ENTRY.toNewcomer) return false; + } + }, + + // BR_G70 + ruleCertificationToLeaver: (cindex) => { + for (const ENTRY of cindex) { + if (ENTRY.toLeaver) return false; + } + }, + + // BR_G71 + ruleCertificationReplay: (cindex) => { + for (const ENTRY of cindex) { + if (ENTRY.isReplay) return false; + } + }, + + // BR_G72 + ruleCertificationSignature: (cindex) => { + for (const ENTRY of cindex) { + if (!ENTRY.sigOK) return false; + } + }, + + // BR_G73 + ruleIdentityUIDUnicity: (iindex) => { + for (const ENTRY of iindex) { + if (!ENTRY.uidUnique) return false; + } + }, + + // BR_G74 + ruleIdentityPubkeyUnicity: (iindex) => { + for (const ENTRY of iindex) { + if (!ENTRY.pubUnique) return false; + } + }, + + // BR_G75 + ruleMembershipSuccession: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.numberFollowing) return false; + } + }, + + // BR_G76 + ruleMembershipDistance: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.distanceOK) return false; + } + }, + + // BR_G77 + ruleMembershipOnRevoked: (mindex) => { + for (const ENTRY of mindex) { + if (ENTRY.onRevoked) return false; + } + }, + + // BR_G78 + ruleMembershipJoinsTwice: (mindex) => { + for (const ENTRY of mindex) { + if (ENTRY.joinsTwice) return false; + } + }, + + // BR_G79 + ruleMembershipEnoughCerts: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.enoughCerts) return false; + } + }, + + // BR_G80 + ruleMembershipLeaverIsMember: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.leaverIsMember) return false; + } + }, + + // BR_G81 + ruleMembershipActiveIsMember: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.activeIsMember) return false; + } + }, + + // BR_G82 + ruleMembershipRevokedIsMember: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.revokedIsMember) return false; + } + }, + + // BR_G83 + ruleMembershipRevokedSingleton: (mindex) => { + for (const ENTRY of mindex) { + if (ENTRY.alreadyRevoked) return false; + } + }, + + // BR_G84 + ruleMembershipRevocationSignature: (mindex) => { + for (const ENTRY of mindex) { + if (!ENTRY.revocationSigOK) return false; + } + }, + + // BR_G85 + ruleMembershipExcludedIsMember: (iindex) => { + for (const ENTRY of iindex) { + if (!ENTRY.excludedIsMember) return false; + } + }, + + // BR_G86 + ruleToBeKickedArePresent: (mindex, dal) => co(function*() { + const toBeKicked = yield dal.iindexDAL.getToBeKickedPubkeys(); + for (const toKick of toBeKicked) { + if (count(_.where(mindex, { pub: toKick, isBeingKicked: true })) !== 1) { + return false; + } + } + }), + + // BR_G103 + ruleTxWritability: (sindex) => { + for (const ENTRY of sindex) { + if (ENTRY.age > constants.TRANSACTION_EXPIRY_DELAY) return false; + } + }, + + // BR_G87 + ruleInputIsAvailable: (sindex) => { + const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + for (const ENTRY of inputs) { + if (!ENTRY.available) { + return false; + } + } + }, + + // BR_G88 + ruleInputIsUnlocked: (sindex) => { + const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + for (const ENTRY of inputs) { + if (ENTRY.isLocked) { + return false; + } + } + }, + + // BR_G89 + ruleInputIsTimeUnlocked: (sindex) => { + const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + for (const ENTRY of inputs) { + if (ENTRY.isTimeLocked) { + return false; + } + } + }, + + // BR_G90 + ruleOutputBase: (sindex, HEAD_1) => { + const inputs = _.where(sindex, { op: constants.IDX_CREATE }); + for (const ENTRY of inputs) { + if (ENTRY.unitBase > HEAD_1.unitBase) { + return false; + } + } + }, + + // BR_G91 + ruleIndexGenDividend: (HEAD, dal) => co(function*() { + const dividends = []; + if (HEAD.new_dividend) { + const potentials = reduceBy(yield dal.iindexDAL.sqlFind({ member: true }), ['pub']); + for (const potential of potentials) { + const MEMBER = reduce(yield dal.iindexDAL.reducable(potential.pub)); + if (MEMBER.member) { + dividends.push({ + op: 'CREATE', + identifier: MEMBER.pub, + pos: HEAD.number, + written_on: [HEAD.number, HEAD.hash].join('-'), + written_time: HEAD.medianTime, + amount: HEAD.dividend, + base: HEAD.unitBase, + locktime: null, + conditions: 'SIG(' + MEMBER.pub + ')', + consumed: false + }); + } + } + } + return dividends; + }), + + // BR_G92 + ruleIndexGenCertificationExpiry: (HEAD, dal) => co(function*() { + const expiries = []; + const certs = yield dal.cindexDAL.findExpired(HEAD.medianTime); + for (const CERT of certs) { + expiries.push({ + op: 'UPDATE', + issuer: CERT.issuer, + receiver: CERT.receiver, + created_on: CERT.created_on, + written_on: [HEAD.number, HEAD.hash].join('-'), + expired_on: HEAD.medianTime + }); + } + return expiries; + }), + + // BR_G93 + ruleIndexGenMembershipExpiry: (HEAD, dal) => co(function*() { + const expiries = []; + const memberships = reduceBy(yield dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime } }), ['pub']); + for (const POTENTIAL of memberships) { + const MS = yield dal.mindexDAL.getReducedMS(POTENTIAL.pub); + const hasRenewedSince = MS.expires_on > HEAD.medianTime; + if (!MS.expired_on && !hasRenewedSince) { + let shouldBeKicked = true; + // ------ Fast synchronization specific code ------ + const idty = yield dal.iindexDAL.getFromPubkey(POTENTIAL.pub); + if (!idty.member) { + shouldBeKicked = false; + } + // ------------------------------------------------ + if (shouldBeKicked) { + expiries.push({ + op: 'UPDATE', + pub: MS.pub, + created_on: MS.created_on, + written_on: [HEAD.number, HEAD.hash].join('-'), + expired_on: HEAD.medianTime + }); + } + } + } + return expiries; + }), + + // BR_G94 + ruleIndexGenExclusionByMembership: (HEAD, mindex) => co(function*() { + const exclusions = []; + const memberships = _.filter(mindex, (entry) => entry.expired_on); + for (const MS of memberships) { + exclusions.push({ + op: 'UPDATE', + pub: MS.pub, + written_on: [HEAD.number, HEAD.hash].join('-'), + kick: true + }); + } + return exclusions; + }), + + // BR_G95 + ruleIndexGenExclusionByCertificatons: (HEAD, cindex, conf, dal) => co(function*() { + const exclusions = []; + const expiredCerts = _.filter(cindex, (c) => c.expired_on > 0); + for (const CERT of expiredCerts) { + const just_expired = _.filter(cindex, (c) => c.receiver == CERT.receiver && c.expired_on > 0); + const just_received = _.filter(cindex, (c) => c.receiver == CERT.receiver && c.expired_on == 0); + const non_expired_global = yield dal.cindexDAL.sqlFind({ receiver: CERT.receiver, expired_on: 0 }); + if ((count(non_expired_global) - count(just_expired) + count(just_received)) < conf.sigQty) { + exclusions.push({ + op: 'UPDATE', + pub: CERT.receiver, + written_on: [HEAD.number, HEAD.hash].join('-'), + kick: true + }); + } + } + return exclusions; + }), + + // BR_G96 + ruleIndexGenImplicitRevocation: (HEAD, dal) => co(function*() { + const revocations = []; + const pending = yield dal.mindexDAL.sqlFind({ revokes_on: { $lte: HEAD.medianTime}, revoked_on: { $null: true } }); + for (const MS of pending) { + const REDUCED = reduce(yield dal.mindexDAL.sqlFind({ pub: MS.pub })); + if (REDUCED.revokes_on <= HEAD.medianTime && !REDUCED.revoked_on) { + revocations.push({ + op: 'UPDATE', + pub: MS.receiver, + written_on: [HEAD.number, HEAD.hash].join('-'), + revoked_on: HEAD.medianTime + }); + } + } + return revocations; + }), + + // BR_G104 + ruleIndexCorrectMembershipExpiryDate: (HEAD, mindex, dal) => co(function*() { + for (const MS of mindex) { + if (MS.type == 'JOIN' || MS.type == 'ACTIVE') { + let basedBlock = { medianTime: 0 }; + if (HEAD.number == 0) { + basedBlock = HEAD; + } else { + basedBlock = yield dal.getBlockByBlockstamp(MS.created_on); + } + MS.expires_on += basedBlock.medianTime; + MS.revokes_on += basedBlock.medianTime; + } + } + }), + + // BR_G105 + ruleIndexCorrectCertificationExpiryDate: (HEAD, cindex, dal) => co(function*() { + for (const CERT of cindex) { + let basedBlock = { medianTime: 0 }; + if (HEAD.number == 0) { + basedBlock = HEAD; + } else { + basedBlock = yield dal.getBlock(CERT.created_on); + } + CERT.expires_on += basedBlock.medianTime; + } + }), + + iindexCreate: (index) => _(index).filter({ index: constants.I_INDEX, op: constants.IDX_CREATE }), + mindexCreate: (index) => _(index).filter({ index: constants.M_INDEX, op: constants.IDX_CREATE }), + iindex: (index) => _(index).filter({ index: constants.I_INDEX }), + mindex: (index) => _(index).filter({ index: constants.M_INDEX }), + cindex: (index) => _(index).filter({ index: constants.C_INDEX }), + sindex: (index) => _(index).filter({ index: constants.S_INDEX }), + + DUP_HELPERS: { + + reduce: reduce, + reduceBy: reduceBy, + getMaxBlockSize: (HEAD) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)), + checkPeopleAreNotOudistanced: checkPeopleAreNotOudistanced + } +}; + +function count(range) { + return range.length; +} + +function uniq(range) { + return _.uniq(range); +} + +function average(values) { + // No values => 0 average + if (!values.length) return 0; + // Otherwise, real average + const avg = values.reduce((sum, val) => sum + val, 0) / values.length; + return Math.floor(avg); +} + +function median(values) { + let med = null; + values.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0)); + const nbValues = values.length; + if (nbValues % 2 === 0) { + // Even number: the median is the average between the 2 central values, ceil rounded. + const firstValue = values[nbValues / 2]; + const secondValue = values[nbValues / 2 - 1]; + med = ((firstValue + secondValue) / 2); // TODO v1.0 median ceil rounded + } else { + med = values[(nbValues + 1) / 2 - 1]; + } + return med; +} + +function number(blockstamp) { + return parseInt(blockstamp); +} + +function blockstamp(number, hash) { + return [number, hash].join('-'); +} + +function reduce(records) { + return records.reduce((obj, record) => { + const keys = Object.keys(record); + for (const k of keys) { + if (record[k] !== undefined && record[k] !== null) { + obj[k] = record[k]; + } + } + return obj; + }, {}); +} + +function reduceBy(reducables, properties) { + const reduced = reducables.reduce((map, entry) => { + const id = properties.map((prop) => entry[prop]).join('-'); + map[id] = map[id] || []; + map[id].push(entry); + return map; + }, {}); + return Object.values(reduced).map((value) => indexer.DUP_HELPERS.reduce(value)); +} + +function checkPeopleAreNotOudistanced (version, pubkeys, newLinks, newcomers, conf, dal) { + return co(function *() { + let wotb = dal.wotb; + let current = yield dal.getCurrentBlockOrNull(); + let membersCount = current ? current.membersCount : 0; + // TODO: make a temporary copy of the WoT in RAM + // We add temporarily the newcomers to the WoT, to integrate their new links + let nodesCache = newcomers.reduce((map, pubkey) => { + let nodeID = wotb.addNode(); + map[pubkey] = nodeID; + wotb.setEnabled(false, nodeID); // These are not members yet + return map; + }, {}); + // Add temporarily the links to the WoT + let tempLinks = []; + let toKeys = _.keys(newLinks); + for (const toKey of toKeys) { + let toNode = yield getNodeIDfromPubkey(nodesCache, toKey, dal); + for (const fromKey of newLinks[toKey]) { + let fromNode = yield getNodeIDfromPubkey(nodesCache, fromKey, dal); + tempLinks.push({ from: fromNode, to: toNode }); + } + } + tempLinks.forEach((link) => wotb.addLink(link.from, link.to)); + // Checking distance of each member against them + let error; + for (const pubkey of pubkeys) { + let nodeID = yield getNodeIDfromPubkey(nodesCache, pubkey, dal); + let dSen; + if (version <= 3) { + dSen = Math.ceil(constants.CONTRACT.DSEN_P * Math.exp(Math.log(membersCount) / conf.stepMax)); + } else { + dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax)); + } + let isOutdistanced = wotb.isOutdistanced(nodeID, dSen, conf.stepMax, conf.xpercent); + if (isOutdistanced) { + error = Error('Joiner/Active is outdistanced from WoT'); + break; + } + } + // Undo temp links/nodes + tempLinks.forEach((link) => wotb.removeLink(link.from, link.to)); + newcomers.forEach(() => wotb.removeNode()); + return error ? true : false; + }); +} + +function getNodeIDfromPubkey(nodesCache, pubkey, dal) { + return co(function *() { + let toNode = nodesCache[pubkey]; + // Eventually cache the target nodeID + if (toNode === null || toNode === undefined) { + let idty = yield dal.getWrittenIdtyByPubkey(pubkey); + toNode = idty.wotb_id; + nodesCache[pubkey] = toNode; + } + return toNode; + }); +} + +function sigCheckRevoke(entry, dal, currency) { + return co(function*() { + try { + let pubkey = entry.pub, sig = entry.revocation; + let idty = yield dal.getWrittenIdtyByPubkey(pubkey); + if (!idty) { + throw Error("A pubkey who was never a member cannot be revoked"); + } + if (idty.revoked) { + throw Error("A revoked identity cannot be revoked again"); + } + let rawRevocation = rawer.getOfficialRevocation({ + currency: currency, + issuer: idty.pubkey, + uid: idty.uid, + buid: idty.buid, + sig: idty.sig, + revocation: '' + }); + let sigOK = keyring.verify(rawRevocation, sig, pubkey); + if (!sigOK) { + throw Error("Revocation signature must match"); + } + return true; + } catch (e) { + return false; + } + }); +} + + + +function checkCertificationIsValid (block, cert, findIdtyFunc, conf, dal) { + return co(function *() { + if (block.number == 0 && cert.created_on != 0) { + throw Error('Number must be 0 for root block\'s certifications'); + } else { + try { + let basedBlock = { + hash: constants.BLOCK.SPECIAL_HASH + }; + if (block.number != 0) { + try { + basedBlock = yield dal.getBlock(cert.created_on); + } catch (e) { + throw Error('Certification based on an unexisting block'); + } + } + let idty = yield findIdtyFunc(block, cert.to, dal); + let current = block.number == 0 ? null : yield dal.getCurrentBlockOrNull(); + if (!idty) { + throw Error('Identity does not exist for certified'); + } + else if (current && current.medianTime > basedBlock.medianTime + conf.sigValidity) { + throw Error('Certification has expired'); + } + else if (cert.from == idty.pubkey) + throw Error('Rejected certification: certifying its own self-certification has no meaning'); + else { + const buid = [cert.created_on, basedBlock.hash].join('-'); + idty.currency = conf.currency; + const raw = rawer.getOfficialCertification(_.extend(idty, { + idty_issuer: idty.pubkey, + idty_uid: idty.uid, + idty_buid: idty.buid, + idty_sig: idty.sig, + issuer: cert.from, + buid: buid, + sig: '' + })); + const verified = keyring.verify(raw, cert.sig, cert.from); + if (!verified) { + throw Error('Wrong signature for certification'); + } + return true; + } + } catch (e) { + return false; + } + } + }); +} + +function txSourceUnlock(ENTRY, source) { + const tx = ENTRY.txObj; + let sigResults = require('../rules/local_rules').HELPERS.getSigResult(tx, 'a'); + let unlocksForCondition = []; + let unlockValues = ENTRY.unlock; + if (source.conditions) { + if (unlockValues) { + // Evaluate unlock values + let sp = unlockValues.split(' '); + for (const func of sp) { + let param = func.match(/\((.+)\)/)[1]; + if (func.match(/SIG/)) { + let pubkey = tx.issuers[parseInt(param)]; + if (!pubkey) { + return false; + } + unlocksForCondition.push({ + pubkey: pubkey, + sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false + }); + } else { + // XHX + unlocksForCondition.push(param); + } + } + } + if (unlock(source.conditions, unlocksForCondition)) { + return true; + } + } + return false; +} diff --git a/app/lib/entity/block.js b/app/lib/entity/block.js index edec7ccaceaacbda34f0ba9483841860fe6da846..760e96b41aa41c8b4cc689ffe7137ad5480e0658 100644 --- a/app/lib/entity/block.js +++ b/app/lib/entity/block.js @@ -162,33 +162,6 @@ function Block(json) { return found; }; - this.isLeaving = (pubkey) => { - let i = 0; - let found = false; - while (!found && i < this.leavers.length) { - if (this.leavers[i].match(new RegExp('^' + pubkey))) - found = true; - i++; - } - while (!found && i < this.excluded.length) { - if (this.excluded[i].match(new RegExp('^' + pubkey))) - found = true; - i++; - } - return found; - }; - - this.isJoining = (pubkey) => { - let i = 0; - let found = false; - while (!found && i < this.joiners.length) { - if (this.joiners[i].match(new RegExp('^' + pubkey))) - found = true; - i++; - } - return found; - }; - this.getTransactions = () => { const transactions = []; const version = this.version; @@ -206,7 +179,7 @@ function Block(json) { base: this.version >= 3 ? sp[1] : null, type: this.version >= 3 ? sp[2] : sp[0], identifier: this.version >= 3 ? sp[3] : sp[1], - noffset: this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]), + pos: this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]), raw: input }); }); @@ -245,3 +218,32 @@ Block.statics.getLen = (block) => block.identities.length + block.revoked.length + block.certifications.length + block.transactions.reduce((sum, tx) => sum + Transaction.statics.getLen(tx), 0); + +Block.statics.getHash = (block) => { + const entity = Block.statics.fromJSON(block); + return entity.getHash(); +}; + +Block.statics.getConf = (block) => { + const sp = block.parameters.split(':'); + const bconf = {}; + bconf.c = parseFloat(sp[0]); + bconf.dt = parseInt(sp[1]); + bconf.ud0 = parseInt(sp[2]); + bconf.sigPeriod = parseInt(sp[3]); + bconf.sigStock = parseInt(sp[4]); + bconf.sigWindow = parseInt(sp[5]); + bconf.sigValidity = parseInt(sp[6]); + bconf.sigQty = parseInt(sp[7]); + bconf.idtyWindow = parseInt(sp[8]); + bconf.msWindow = parseInt(sp[9]); + bconf.xpercent = parseFloat(sp[10]); + bconf.msValidity = parseInt(sp[11]); + bconf.stepMax = parseInt(sp[12]); + bconf.medianTimeBlocks = parseInt(sp[13]); + bconf.avgGenTime = parseInt(sp[14]); + bconf.dtDiffEval = parseInt(sp[15]); + bconf.blocksRot = parseInt(sp[16]); + bconf.percentRot = parseFloat(sp[17]); + return bconf; +}; diff --git a/app/lib/entity/identity.js b/app/lib/entity/identity.js index c2edb5c071abe482517094348c020d5bdf6cb064..d02c23ea3b19373508b5e93cf03ff8a2f979281c 100644 --- a/app/lib/entity/identity.js +++ b/app/lib/entity/identity.js @@ -7,12 +7,9 @@ const Identity = function(json) { this.revoked = false; this.revoked_on = null; - this.currentMSN = -1; - this.currentINN = -1; this.member = false; this.buid = ''; this.kick = false; - this.leaving = false; this.wasMember = false; this.signed = []; this.certs = []; diff --git a/app/lib/entity/link.js b/app/lib/entity/link.js deleted file mode 100644 index e88061d008e2352e57c6464cec0669cba8b3319b..0000000000000000000000000000000000000000 --- a/app/lib/entity/link.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; -const _ = require('underscore'); - -module.exports = Link; - -function Link(json) { - - _(json || {}).keys().forEach((key) => { - let value = json[key]; - if (key == "number") { - value = parseInt(value); - } - this[key] = value; - }); -} \ No newline at end of file diff --git a/app/lib/entity/source.js b/app/lib/entity/source.js index 489832540eb9686ef29b4438e6f51196c5df2fad..a903563aca3432c74c3f850af77c9717be41e6b8 100644 --- a/app/lib/entity/source.js +++ b/app/lib/entity/source.js @@ -19,7 +19,7 @@ function Source(json) { this.json = function () { return { "type": this.type, - "noffset": this.noffset, + "noffset": this.pos, "identifier": this.identifier, "amount": this.amount, "base": this.base diff --git a/app/lib/entity/transaction.js b/app/lib/entity/transaction.js index e2b7d8fc4b50166e029cf9639a635c80540ef3d5..080d14eacd6f206d00712d45b24e00da90b92e08 100644 --- a/app/lib/entity/transaction.js +++ b/app/lib/entity/transaction.js @@ -63,7 +63,7 @@ let Transaction = function(obj, currency) { base: this.version >= 3 ? sp[1] : null, type: this.version >= 3 ? sp[2] : sp[0], identifier: this.version >= 3 ? sp[3] : sp[1], - noffset: this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]), + pos: this.version >= 3 ? parseInt(sp[4]) : parseInt(sp[2]), raw: input }); }); diff --git a/app/lib/rules/global_rules.js b/app/lib/rules/global_rules.js index 814b0331f8fad327ce097c5e1638c795d5948a01..0cabed0a4e9b8ade7b31984b3e8a7e468bb54cae 100644 --- a/app/lib/rules/global_rules.js +++ b/app/lib/rules/global_rules.js @@ -6,6 +6,7 @@ const _ = require('underscore'); const constants = require('../constants'); const keyring = require('../crypto/keyring'); const rawer = require('../ucp/rawer'); +const indexer = require('../dup/indexer'); const Identity = require('../entity/identity'); const Membership = require('../entity/membership'); const Certification = require('../entity/certification'); @@ -16,246 +17,9 @@ const local_rules = require('./local_rules'); let rules = {}; -rules.FUNCTIONS = { - - checkVersion: (block, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - if (current && current.version > 2 && block.version == 2) { - throw Error('`Version: 2` must follow another V2 block or be the root block'); - } - else if (block.version > 2) { - if (current && current.version != (block.version - 1) && current.version != block.version) { - throw Error('`Version: ' + block.version + '` must follow another V' + block.version + ' block, a V' + (block.version - 1) + ' block or be the root block'); - } - } - return true; - }), - - checkBlockLength: (block, dal) => co(function *() { - if (block.len > 500) { - const maxSize = yield rules.HELPERS.getMaxBlockSize(dal); - if (block.len > maxSize) { - throw Error('Block size is too high'); - } - } else { - // There is no problem with blocks <= 500 lines - return true; - } - }), - - checkNumber: (block, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - if (!current && block.number != 0) { - throw Error('Root block required first'); - } - else if (current && block.number <= current.number) { - throw Error('Too late for this block'); - } - else if (current && block.number > current.number + 1) { - throw Error('Too early for this block'); - } - return true; - }), - - checkPoWMin: (block, conf, dal) => co(function *() { - if (block.number > 0) { - let correctPowMin = yield getPoWMinFor(block.version, block.number, conf, dal); - if (block.powMin < correctPowMin) { - throw Error('PoWMin value must be incremented'); - } - else if (correctPowMin < block.powMin) { - throw Error('PoWMin value must be decremented'); - } - } - return true; - }), - - checkProofOfWork: (block, conf, dal) => co(function *() { - // Compute exactly how much zeros are required for this block's issuer - let difficulty = yield getTrialLevel(block.version, block.issuer, conf, dal); - const remainder = difficulty % 16; - const nbZerosReq = Math.max(0, (difficulty - remainder) / 16); - const highMark = constants.PROOF_OF_WORK.UPPER_BOUND[remainder]; - const powRegexp = new RegExp('^0{' + nbZerosReq + '}' + '[0-' + highMark + ']'); - if (!block.hash.match(powRegexp)) { - const givenZeros = Math.max(0, Math.min(nbZerosReq, block.hash.match(/^0*/)[0].length)); - const c = block.hash.substr(givenZeros, 1); - throw Error('Wrong proof-of-work level: given ' + givenZeros + ' zeros and \'' + c + '\', required was ' + nbZerosReq + ' zeros and an hexa char between [0-' + highMark + ']'); - } - return true; - }), - - checkUD: (block, conf, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - let nextUD = yield rules.HELPERS.getNextUD(dal, conf, block.version, block.medianTime, current, block.membersCount); - if (!current && block.dividend) { - throw Error('Root block cannot have UniversalDividend field'); - } - else if (current && nextUD && !block.dividend) { - throw Error('Block must have a UniversalDividend field'); - } - else if (current && nextUD && block.dividend != nextUD.dividend) { - throw Error('UniversalDividend must be equal to ' + nextUD.dividend); - } - else if (current && !nextUD && block.dividend) { - throw Error('This block cannot have UniversalDividend'); - } - else if (current && nextUD && block.unitbase != nextUD.unitbase) { - throw Error('UnitBase must be equal to ' + nextUD.unitbase); - } - else if (block.version > 2 && current && !nextUD && block.unitbase != current.unitbase) { - throw Error('UnitBase must be equal to previous unit base = ' + current.unitbase); - } - return true; - }), - - checkPreviousHash: (block, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - if (current && block.previousHash != current.hash) { - throw Error('PreviousHash not matching hash of current block'); - } - return true; - }), - - checkPreviousIssuer: (block, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - if (current && block.previousIssuer != current.issuer) { - throw Error('PreviousIssuer not matching issuer of current block'); - } - return true; - }), - - checkMembersCountIsGood: (block, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - const currentCount = current ? current.membersCount : 0; - const constiation = block.joiners.length - block.excluded.length; - if (block.membersCount != currentCount + constiation) { - throw Error('Wrong members count'); - } - return true; - }), - - checkIssuerIsMember: (block, dal) => co(function *() { - let isMember = block.number == 0 ? isLocalMember(block.issuer, block) : yield dal.isMember(block.issuer); - if (!isMember) { - throw Error('Issuer is not a member'); - } - return true; - }), - - checkDifferentIssuersCount: (block, conf, dal) => co(function *() { - if (block.version > 2) { - const isGood = block.issuersCount == (yield rules.HELPERS.getDifferentIssuers(dal)); - if (!isGood) { - throw Error('DifferentIssuersCount is not correct'); - } - return isGood; - } - }), - - checkIssuersFrame: (block, conf, dal) => co(function *() { - if (block.version > 2) { - const isGood = block.issuersFrame == (yield rules.HELPERS.getIssuersFrame(dal)); - if (!isGood) { - throw Error('IssuersFrame is not correct'); - } - return isGood; - } - }), - - checkIssuersFrameVar: (block, conf, dal) => co(function *() { - if (block.version > 2) { - const isGood = block.issuersFrameVar == (yield rules.HELPERS.getIssuersFrameVar(block, dal)); - if (!isGood) { - throw Error('IssuersFrameVar is not correct'); - } - return isGood; - } - }), - - checkTimes: (block, conf, dal) => co(function *() { - if (block.number > 0) { - let medianTime = yield getMedianTime(block.number, conf, dal); - if (medianTime != block.medianTime) { - throw Error('Wrong MedianTime'); - } - } - return true; - }), - - checkCertificationsAreMadeByMembers: (block, dal) => co(function *() { - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - let isMember = yield isAMember(cert.from, block, dal); - if (!isMember) { - throw Error('Certification from non-member'); - } - } - return true; - }), - - checkCertificationsAreValid: (block, conf, dal) => co(function *() { - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - yield checkCertificationIsValid(block, cert, (b, pub) => { - return getGlobalIdentity(b, pub, dal); - }, conf, dal); - } - return true; - }), - - checkCertificationsAreMadeToMembers: (block, dal) => co(function *() { - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - let isMember = yield isMemberOrJoiner(cert.to, block, dal); - if (!isMember) { - throw Error('Certification to non-member'); - } - } - return true; - }), - - checkCertificationsAreMadeToNonLeaver: (block, dal) => co(function *() { - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - let isLeaving = yield dal.isLeaving(cert.to); - if (isLeaving) { - throw Error('Certification to leaver'); - } - } - return true; - }), - - checkCertificationsDelayIsRespected: (block, conf, dal) => co(function *() { - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - let previous = yield dal.getPreviousLinks(cert.from, cert.to); - let duration = previous && (block.medianTime - parseInt(previous.timestamp)); - if (previous && (duration <= conf.sigValidity)) { - throw Error('A similar certification is already active'); - } - } - return true; - }), +// TODO: all the global rules should be replaced by index rule someday - checkCertificationsPeriodIsRespected: (block, conf, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - let previous = yield dal.getLastValidFrom(cert.from); - if (previous) { - let duration = current.medianTime - parseInt(previous.timestamp); - if (duration < conf.sigPeriod) { - throw Error('Previous certification is not chainable yet'); - } - let stock = yield dal.getValidLinksFrom(cert.from); - if (stock >= conf.sigStock) { - throw Error('Previous certification is not chainable yet'); - } - } - } - return true; - }), +rules.FUNCTIONS = { checkIdentitiesAreWritable: (block, conf, dal) => co(function *() { let current = yield dal.getCurrentBlockOrNull(); @@ -286,197 +50,6 @@ rules.FUNCTIONS = { return true; }), - checkCertificationsAreWritable: (block, conf, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - for (const obj of block.certifications) { - let cert = Certification.statics.fromInline(obj); - if (current) { - // Because the window rule does not apply on initial certifications - let basedBlock = yield dal.getBlock(cert.block_number); - // Check if writable - let duration = current.medianTime - parseInt(basedBlock.medianTime); - if (duration > conf.sigWindow) { - throw Error('Certification is too old and cannot be written'); - } - } - } - return true; - }), - - checkMembershipsAreWritable: (block, conf, dal) => co(function *() { - let current = yield dal.getCurrentBlockOrNull(); - let fields = ['joiners', 'actives', 'leavers']; - for (const field of fields) { - for (const obj of block[field]) { - let ms = Membership.statics.fromInline(obj); - if (ms.block != constants.BLOCK.SPECIAL_BLOCK) { - let msBasedBlock = yield dal.getBlock(ms.block); - let age = current.medianTime - msBasedBlock.medianTime; - if (age > conf.msWindow) { - throw 'Too old membership'; - } - } - } - } - return true; - }), - - checkIdentityUnicity: (block, conf, dal) => co(function *() { - for (const obj of block.identities) { - let idty = Identity.statics.fromInline(obj); - let found = yield dal.getWrittenIdtyByUID(idty.uid); - if (found) { - throw Error('Identity already used'); - } - } - return true; - }), - - checkPubkeyUnicity: (block, conf, dal) => co(function *() { - for (const obj of block.identities) { - let idty = Identity.statics.fromInline(obj); - let found = yield dal.getWrittenIdtyByPubkey(idty.pubkey); - if (found) { - throw Error('Pubkey already used'); - } - } - return true; - }), - - checkJoiners: (block, conf, dal) => co(function *() { - for (const obj of block.joiners) { - let ms = Membership.statics.fromInline(obj); - yield checkMSTarget(ms, block, conf, dal); - let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); - if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) { - throw Error('Membership\'s number must be greater than last membership of the pubkey'); - } - if (idty && idty.member) { - throw Error('Cannot be in joiners if already a member'); - } - } - return true; - }), - - checkActives: (block, conf, dal) => co(function *() { - for (const obj of block.actives) { - let ms = Membership.statics.fromInline(obj); - yield checkMSTarget(ms, block, conf, dal); - let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); - if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) { - throw Error('Membership\'s number must be greater than last membership of the pubkey'); - } - if (!idty || !idty.member) { - throw Error('Cannot be in actives if not a member'); - } - } - return true; - }), - - checkLeavers: (block, conf, dal) => co(function *() { - for (const obj of block.leavers) { - let ms = Membership.statics.fromInline(obj); - yield checkMSTarget(ms, block, conf, dal); - let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); - if (idty && idty.currentMSN != -1 && idty.currentMSN >= ms.number) { - throw Error('Membership\'s number must be greater than last membership of the pubkey'); - } - if (!idty || !idty.member) { - throw Error('Cannot be in leavers if not a member'); - } - } - return true; - }), - - checkRevoked: (block, conf, dal) => co(function *() { - for (const revoked of block.revoked) { - let sp = revoked.split(':'); - let pubkey = sp[0], sig = sp[1]; - let idty = yield dal.getWrittenIdtyByPubkey(pubkey); - if (!idty) { - throw Error("A pubkey who was never a member cannot be revoked"); - } - if (idty.revoked) { - throw Error("A revoked identity cannot be revoked again"); - } - let rawRevocation = rawer.getOfficialRevocation({ - currency: block.currency, - issuer: idty.pubkey, - uid: idty.uid, - buid: idty.buid, - sig: idty.sig, - revocation: '' - }); - let sigOK = keyring.verify(rawRevocation, sig, pubkey); - if (!sigOK) { - throw Error("Revocation signature must match"); - } - } - return true; - }), - - checkExcluded: (block, conf, dal) => co(function *() { - for (const pubkey of block.excluded) { - let idty = yield dal.getWrittenIdtyByPubkey(pubkey); - if (!idty) { - throw Error("Cannot be in excluded if not a member"); - } - } - return true; - }), - - checkJoinersHaveEnoughCertifications: (block, conf, dal) => co(function *() { - if (block.number > 0) { - const newLinks = getNewLinks(block); - for (const obj of block.joiners) { - let ms = Membership.statics.fromInline(obj); - let links = yield dal.getValidLinksTo(ms.issuer); - let nbCerts = links.length + (newLinks[ms.issuer] || []).length; - if (nbCerts < conf.sigQty) { - throw Error('Joiner/Active does not gathers enough certifications'); - } - } - } - return true; - }), - - checkJoinersAreNotOudistanced: (block, conf, dal) => checkPeopleAreNotOudistanced( - block.version, - block.joiners.map((inlineMS) => Membership.statics.fromInline(inlineMS).issuer), - getNewLinks(block), - block.identities.map((inline) => Identity.statics.fromInline(inline).pubkey), - conf, dal), - - checkActivesAreNotOudistanced: (block, conf, dal) => checkPeopleAreNotOudistanced( - block.version, - block.actives.map((inlineMS) => Membership.statics.fromInline(inlineMS).issuer), - getNewLinks(block), - block.identities.map((inline) => Identity.statics.fromInline(inline).pubkey), - conf, dal), - - checkKickedMembersAreExcluded: (block, conf, dal) => co(function *() { - let identities = yield dal.getToBeKicked(); - let remainingKeys = identities.map(function (idty) { - return idty.pubkey; - }); - remainingKeys = _(remainingKeys).difference(block.excluded); - if (remainingKeys.length > 0) { - throw Error('All kicked members must be present under Excluded members') - } - return true; - }), - - checkJoinersAreNotRevoked: (block, conf, dal) => co(function *() { - for (const obj of block.joiners) { - let ms = Membership.statics.fromInline(obj); - let idty = yield dal.getWrittenIdtyByPubkey(ms.issuer); - if (idty && idty.revoked) { - throw Error('Revoked pubkeys cannot join'); - } - } - return true; - }), - checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => co(function *() { let txs = block.getTransactions(); const current = yield dal.getCurrentBlockOrNull(); @@ -491,8 +64,8 @@ rules.FUNCTIONS = { } for (let k = 0, len2 = tx.inputs.length; k < len2; k++) { let src = tx.inputs[k]; - let dbSrc = yield dal.getSource(src.identifier, src.noffset); - logger.debug('Source %s:%s = %s', src.identifier, src.noffset, dbSrc && dbSrc.consumed); + let dbSrc = yield dal.getSource(src.identifier, src.pos); + logger.debug('Source %s:%s = %s', src.identifier, src.pos, dbSrc && dbSrc.consumed); if (!dbSrc && alsoCheckPendingTransactions) { // For chained transactions which are checked on sandbox submission, we accept them if there is already // a previous transaction of the chain already recorded in the pool @@ -500,7 +73,7 @@ rules.FUNCTIONS = { let hypotheticSrc = null; let targetTX = yield dal.getTxByHash(src.identifier); if (targetTX) { - let outputStr = targetTX.outputs[src.noffset]; + let outputStr = targetTX.outputs[src.pos]; if (outputStr) { hypotheticSrc = Transaction.statics.outputStr2Obj(outputStr); hypotheticSrc.consumed = false; @@ -511,11 +84,11 @@ rules.FUNCTIONS = { }); } if (!dbSrc || dbSrc.consumed) { - logger.warn('Source ' + [src.type, src.identifier, src.noffset].join(':') + ' is not available'); + logger.warn('Source ' + [src.type, src.identifier, src.pos].join(':') + ' is not available'); throw constants.ERRORS.SOURCE_ALREADY_CONSUMED; } sumOfInputs += dbSrc.amount * Math.pow(10, dbSrc.base); - if (block.medianTime - dbSrc.time < tx.locktime) { + if (block.medianTime - dbSrc.written_time < tx.locktime) { throw constants.ERRORS.LOCKTIME_PREVENT; } let sigResults = local_rules.HELPERS.getSigResult(tx); @@ -547,7 +120,7 @@ rules.FUNCTIONS = { throw Error('Locked'); } } catch (e) { - logger.warn('Source ' + [src.type, src.identifier, src.noffset].join(':') + ' unlock fail'); + logger.warn('Source ' + [src.type, src.identifier, src.pos].join(':') + ' unlock fail'); throw constants.ERRORS.WRONG_UNLOCKER; } } @@ -564,13 +137,6 @@ rules.FUNCTIONS = { } } return true; - }), - - checkTransactionsBlockStamp: (block, conf, dal) => co(function *() { - for(const tx of block.getTransactions()) { - yield rules.HELPERS.checkTxBlockStamp(tx, dal); - } - return true; }) }; @@ -588,19 +154,12 @@ rules.HELPERS = { return Q(false); } try { - yield checkPeopleAreNotOudistanced(version, [member], newLinks, newcomers, conf, dal); - return false; + return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced(version, [member], newLinks, newcomers, conf, dal); } catch (e) { return true; } }), - getTrialLevel: (version, issuer, conf, dal) => getTrialLevel(version, issuer, conf, dal), - - getPoWMin: (version, blockNumber, conf, dal) => getPoWMinFor(version, blockNumber, conf, dal), - - getMedianTime: (blockNumber, conf, dal) => getMedianTime(blockNumber, conf, dal), - checkExistsUserID: (uid, dal) => dal.getWrittenIdtyByUID(uid), checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub), @@ -610,64 +169,6 @@ rules.HELPERS = { medianTime: block.medianTime }, conf, dal, alsoCheckPendingTransactions), - getNextUD: (dal, conf, version, nextMedianTime, current, nextN) => co(function *() { - const lastUDBlock = yield dal.lastUDBlock(); - let lastUDTime = lastUDBlock && lastUDBlock.UDTime; - if (!lastUDTime) { - const rootBlock = yield dal.getBlock(0); - lastUDTime = (rootBlock != null ? rootBlock.medianTime : 0); - } - if (lastUDTime == null) { - return null; - } - if (!current) { - return null; - } - if (lastUDTime + conf.dt <= nextMedianTime) { - const M = lastUDBlock ? lastUDBlock.monetaryMass : current.monetaryMass || 0; - const c = conf.c; - const N = nextN; - const previousUD = lastUDBlock ? lastUDBlock.dividend : conf.ud0; - const previousUB = lastUDBlock ? lastUDBlock.unitbase : constants.FIRST_UNIT_BASE; - if (version == 2) { - if (N > 0) { - const block = { - dividend: Math.ceil(Math.max(previousUD, c * M / Math.pow(10, previousUB) / N)), - unitbase: previousUB - }; - if (block.dividend >= Math.pow(10, constants.NB_DIGITS_UD)) { - block.dividend = Math.ceil(block.dividend / 10.0); - block.unitbase++; - } - return block; - } else { - // The community has collapsed. RIP. - return null; - } - } else { - const block = { - unitbase: previousUB - }; - if (version == 3) { - block.dividend = parseInt(((1 + c) * previousUD).toFixed(0)); - } else { - if (N > 0) { - block.dividend = parseInt((previousUD + Math.pow(c, 2) * (M / N) / Math.pow(10, previousUB)).toFixed(0)); - } else { - // The community has collapsed. RIP. - return null; - } - } - if (block.dividend >= Math.pow(10, constants.NB_DIGITS_UD)) { - block.dividend = Math.ceil(block.dividend / 10.0); - block.unitbase++; - } - return block; - } - } - return null; - }), - checkTxBlockStamp: (tx, dal) => co(function *() { if (tx.version >= 3) { const number = tx.blockstamp.split('-')[0]; @@ -683,81 +184,6 @@ rules.HELPERS = { throw "Transaction has expired"; } } - }), - - getDifferentIssuers: (dal) => co(function *() { - const current = yield dal.getCurrentBlockOrNull(); - let frameSize = 0; - let currentNumber = 0; - if (current) { - currentNumber = current.number; - if (current.version == 2) { - frameSize = 40; - } else { - frameSize = current.issuersFrame; - } - } - const blocksBetween = yield dal.getBlocksBetween(Math.max(0, currentNumber - frameSize + 1), currentNumber); - const issuers = _.pluck(blocksBetween, 'issuer'); - return _.uniq(issuers).length; - }), - - getIssuersFrame: (dal) => co(function *() { - const current = yield dal.getCurrentBlockOrNull(); - let frame = 1; - if (!current) { - frame = 1; - } - else { - if (current.version == 2) { - frame = 40; - } - else if (current.version > 2) { - frame = current.issuersFrame; - // CONVERGENCE - if (current.issuersFrameVar > 0) { - frame++; - } - if (current.issuersFrameVar < 0) { - frame--; - } - } - } - return frame; - }), - - getIssuersFrameVar: (block, dal) => co(function *() { - const current = yield dal.getCurrentBlockOrNull(); - let frameVar = 0; - if (current && current.version > 2) { - frameVar = current.issuersFrameVar; - // CONVERGENCE - if (current.issuersFrameVar > 0) { - frameVar--; - } - if (current.issuersFrameVar < 0) { - frameVar++; - } - // NEW_ISSUER_INC - if (current.issuersCount < block.issuersCount) { - frameVar += 5; - } - // GONE_ISSUER_DEC - if (current.issuersCount > block.issuersCount) { - frameVar -= 5; - } - } - return frameVar; - }), - - getMaxBlockSize: (dal) => co(function *() { - const current = yield dal.getCurrentBlockOrNull(); - const start = current ? current.number - current.issuersCount : 0; - const end = current ? current.number : 0; - const blocks = yield dal.getBlocksBetween(start, end); - const avgSize = blocks.length ? blocks.reduce((lenSum, b) => lenSum + b.len, 0) / blocks.length : 0; - const maxSize = Math.ceil(1.1 * avgSize); - return Math.max(500, maxSize); }) }; @@ -767,37 +193,6 @@ rules.HELPERS = { * *****************************/ -/** - * Get an identity, using global scope. - * Considers identity collision + existence have already been checked. - **/ -function getGlobalIdentity (block, pubkey, dal) { - return co(function *() { - let localInlineIdty = block.getInlineIdentity(pubkey); - if (localInlineIdty) { - return Identity.statics.fromInline(localInlineIdty); - } - return dal.getWrittenIdtyByPubkey(pubkey); - }); -} - -function isAMember(pubkey, block, dal) { - if (block.number == 0) { - return Q(isLocalMember(pubkey, block)); - } else { - return dal.isMember(pubkey); - } -} - -function isMemberOrJoiner(pubkey, block, dal) { - let isJoiner = isLocalMember(pubkey, block); - return isJoiner ? Q(isJoiner) : dal.isMember(pubkey); -} - -function isLocalMember(pubkey, block) { - return block.isJoining(pubkey); -} - function checkMSTarget (ms, block, conf, dal) { return co(function *() { if (block.number == 0 && ms.number != 0) { @@ -807,7 +202,7 @@ function checkMSTarget (ms, block, conf, dal) { throw Error('Hash must be E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 for root block\'s memberships'); } else if (block.number == 0) { - return true; // Valid for root block + return null; // Valid for root block } else { let basedBlock; try { @@ -819,7 +214,7 @@ function checkMSTarget (ms, block, conf, dal) { if (current && current.medianTime > basedBlock.medianTime + conf.msValidity) { throw Error('Membership has expired'); } - return true; + return basedBlock; } }); } @@ -873,314 +268,4 @@ function checkCertificationIsValid (block, cert, findIdtyFunc, conf, dal) { }); } -function checkPeopleAreNotOudistanced (version, pubkeys, newLinks, newcomers, conf, dal) { - return co(function *() { - let wotb = dal.wotb; - let current = yield dal.getCurrentBlockOrNull(); - let membersCount = current ? current.membersCount : 0; - // TODO: make a temporary copy of the WoT in RAM - // We add temporarily the newcomers to the WoT, to integrate their new links - let nodesCache = newcomers.reduce((map, pubkey) => { - let nodeID = wotb.addNode(); - map[pubkey] = nodeID; - wotb.setEnabled(false, nodeID); // These are not members yet - return map; - }, {}); - // Add temporarily the links to the WoT - let tempLinks = []; - let toKeys = _.keys(newLinks); - for (const toKey of toKeys) { - let toNode = yield getNodeIDfromPubkey(nodesCache, toKey, dal); - for (const fromKey of newLinks[toKey]) { - let fromNode = yield getNodeIDfromPubkey(nodesCache, fromKey, dal); - tempLinks.push({ from: fromNode, to: toNode }); - } - } - tempLinks.forEach((link) => wotb.addLink(link.from, link.to)); - // Checking distance of each member against them - let error; - for (const pubkey of pubkeys) { - let nodeID = yield getNodeIDfromPubkey(nodesCache, pubkey, dal); - let dSen; - if (version <= 3) { - dSen = Math.ceil(constants.CONTRACT.DSEN_P * Math.exp(Math.log(membersCount) / conf.stepMax)); - } else { - dSen = Math.ceil(Math.pow(membersCount, 1 / conf.stepMax)); - } - let isOutdistanced = wotb.isOutdistanced(nodeID, dSen, conf.stepMax, conf.xpercent); - if (isOutdistanced) { - error = Error('Joiner/Active is outdistanced from WoT'); - break; - } - } - // Undo temp links/nodes - tempLinks.forEach((link) => wotb.removeLink(link.from, link.to)); - newcomers.forEach(() => wotb.removeNode()); - if (error) { - throw error; - } - }); -} - -function getNodeIDfromPubkey(nodesCache, pubkey, dal) { - return co(function *() { - let toNode = nodesCache[pubkey]; - // Eventually cache the target nodeID - if (toNode === null || toNode === undefined) { - let idty = yield dal.getWrittenIdtyByPubkey(pubkey); - toNode = idty.wotb_id; - nodesCache[pubkey] = toNode; - } - return toNode; - }); -} - -function getTrialLevel (version, issuer, conf, dal) { - return co(function *() { - if (version == 2) { - // Compute exactly how much zeros are required for this block's issuer - let percentRot = conf.percentRot; - let current = yield dal.getCurrentBlockOrNull(); - if (!current) { - return conf.powMin || 0; - } - let last = yield dal.lastBlockOfIssuer(issuer); - let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal); - let issuers = []; - if (last) { - let blocksBetween = yield dal.getBlocksBetween(last.number - 1 - conf.blocksRot, last.number - 1); - issuers = _.pluck(blocksBetween, 'issuer'); - } else { - // So we can have nbPreviousIssuers = 0 & nbBlocksSince = 0 for someone who has never written any block - last = { number: current.number }; - } - const nbPreviousIssuers = _(_(issuers).uniq()).without(issuer).length; - const nbBlocksSince = current.number - last.number; - let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * (1 + nbPreviousIssuers) / (1 + nbBlocksSince))); - if (personal_diff + 1 % 16 == 0) { - personal_diff++; - } - return personal_diff; - } else if (version > 2 && version < 5) { - // Compute exactly how much zeros are required for this block's issuer - let percentRot = conf.percentRot; - let current = yield dal.getCurrentBlockOrNull(); - if (!current) { - return conf.powMin || 0; - } - let last = yield dal.lastBlockOfIssuer(issuer); - let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal); - let nbPreviousIssuers = 0; - if (last) { - nbPreviousIssuers = last.issuersCount; - } else { - // So we have nbBlocksSince = 0 for someone who has never written any block - last = { number: current.number }; - } - const nbBlocksSince = current.number - last.number; - let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * nbPreviousIssuers / (1 + nbBlocksSince))); - if (version > 3) { - const from = current.number - current.issuersFrame + 1; - const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from); - const personal_excess = Math.max(0, (nbBlocksIssuedInFrame / 5) - 1); - // Personal_handicap - personal_diff += Math.floor(Math.log(1 + personal_excess) / Math.log(1.189)); - } - if (personal_diff + 1 % 16 == 0) { - personal_diff++; - } - return personal_diff; - } else if (version == 5) { - // NB: no more use conf.percentRot - // Compute exactly how much zeros are required for this block's issuer - let current = yield dal.getCurrentBlockOrNull(); - if (!current) { - return conf.powMin || 0; - } - let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal); - let blocksBetween = []; - if (current) { - blocksBetween = yield dal.getBlocksBetween(current.number - current.issuersFrame + 1, current.number); - } - const blocksByIssuer = blocksBetween.reduce((oMap, block) => { - oMap[block.issuer] = oMap[block.issuer] || 0; - oMap[block.issuer]++; - return oMap; - }, {}); - const counts = Object.values(blocksByIssuer); - let medianOfIssuedBlocks = null; - counts.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0)); - const nbIssuers = counts.length; - if (nbIssuers % 2 === 0) { - // Even number of nodes: the median is the average between the 2 central values - const firstValue = counts[nbIssuers / 2]; - const secondValue = counts[nbIssuers / 2 - 1]; - medianOfIssuedBlocks = (firstValue + secondValue) / 2; - } else { - medianOfIssuedBlocks = counts[(nbIssuers + 1) / 2 - 1]; - } - - const from = current.number - current.issuersFrame + 1; - const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from); - const personal_excess = Math.max(0, ((nbBlocksIssuedInFrame + 1)/ medianOfIssuedBlocks) - 1); - // Personal_handicap - const handicap = Math.floor(Math.log(1 + personal_excess) / Math.log(1.189)); - let personal_diff = powMin + handicap; - if (personal_diff + 1 % 16 == 0) { - personal_diff++; - } - return personal_diff; - } else if (version >= 6) { - // NB: no more use conf.percentRot - // Compute exactly how much zeros are required for this block's issuer - let current = yield dal.getCurrentBlockOrNull(); - if (!current) { - return conf.powMin || 0; - } - let powMin = yield getPoWMinFor(version, current.number + 1, conf, dal); - let blocksBetween = []; - if (current) { - blocksBetween = yield dal.getBlocksBetween(current.number - current.issuersFrame + 1, current.number); - } - const blocksByIssuer = blocksBetween.reduce((oMap, block) => { - oMap[block.issuer] = oMap[block.issuer] || 0; - oMap[block.issuer]++; - return oMap; - }, {}); - const counts = Object.values(blocksByIssuer); - let medianOfIssuedBlocks = null; - counts.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0)); - const nbIssuers = counts.length; - if (nbIssuers % 2 === 0) { - // Even number of nodes: the median is the average between the 2 central values - const firstValue = counts[nbIssuers / 2]; - const secondValue = counts[nbIssuers / 2 - 1]; - medianOfIssuedBlocks = (firstValue + secondValue) / 2; - } else { - medianOfIssuedBlocks = counts[(nbIssuers + 1) / 2 - 1]; - } - - const from = current.number - current.issuersFrame + 1; - const nbBlocksIssuedInFrame = yield dal.getNbIssuedInFrame(issuer, from); - const personal_excess = Math.max(0, ((nbBlocksIssuedInFrame + 1)/ medianOfIssuedBlocks) - 1); - // Personal_handicap - const handicap = Math.floor(Math.log(1 + personal_excess) / Math.log(1.189)); - - //-- 0.4 reintroduction - let percentRot = conf.percentRot; - let last = yield dal.lastBlockOfIssuer(issuer); - let nbPreviousIssuers = 0; - if (last) { - nbPreviousIssuers = last.issuersCount; - } else { - // So we have nbBlocksSince = 0 for someone who has never written any block - last = { number: current.number }; - } - const nbBlocksSince = current.number - last.number; - - let personal_diff = Math.max(powMin, powMin * Math.floor(percentRot * nbPreviousIssuers / (1 + nbBlocksSince))) + handicap; - if ((personal_diff + 1) % 16 == 0) { - personal_diff++; - } - return personal_diff; - } - }); -} - -/** - * Deduce the PoWMin field for a given block number - */ -function getPoWMinFor (blockVersion, blockNumber, conf, dal) { - return Q.Promise(function(resolve, reject){ - if (blockNumber == 0) { - reject('Cannot deduce PoWMin for block#0'); - } else if (blockNumber % conf.dtDiffEval != 0) { - co(function *() { - const previous = yield dal.getBlock(blockNumber - 1); - return previous.powMin; - }) - .then(resolve) - .catch(function(err) { - reject(err); - throw err; - }); - } else { - co(function *() { - const previous = yield dal.getBlock(blockNumber - 1); - const medianTime = yield getMedianTime(blockNumber, conf, dal); - const speedRange = Math.min(conf.dtDiffEval, blockNumber); - const lastDistant = yield dal.getBlock(Math.max(0, blockNumber - speedRange)); - // Compute PoWMin value - const duration = medianTime - lastDistant.medianTime; - const speed = speedRange / duration; - const ratio = blockVersion > 3 ? constants.POW_DIFFICULTY_RANGE_RATIO_V4 : constants.POW_DIFFICULTY_RANGE_RATIO_V3; - const maxGenTime = Math.ceil(conf.avgGenTime * ratio); - const minGenTime = Math.floor(conf.avgGenTime / ratio); - const maxSpeed = 1.0 / minGenTime; - const minSpeed = 1.0 / maxGenTime; - // logger.debug('Current speed is', speed, '(' + conf.dtDiffEval + '/' + duration + ')', 'and must be [', minSpeed, ';', maxSpeed, ']'); - if (speed >= maxSpeed) { - // Must increase difficulty - if ((previous.powMin + 2) % 16 == 0) { - // Avoid (16*n - 1) value - resolve(previous.powMin + 2); - } else { - resolve(previous.powMin + 1); - } - } - else if (speed <= minSpeed) { - // Must decrease difficulty - if (previous.powMin % 16 == 0) { - // Avoid (16*n - 1) value - resolve(Math.max(0, previous.powMin - 2)); - } else { - resolve(Math.max(0, previous.powMin - 1)); - } - } - else { - // Must not change difficulty - resolve(previous.powMin); - } - }) - .catch(reject); - } - }); -} - -function getMedianTime (blockNumber, conf, dal) { - return co(function *() { - if (blockNumber == 0) { - // No rule to check for block#0 - return 0; - } - // Get the number of blocks we can look back from this block - let blocksCount = blockNumber < conf.medianTimeBlocks ? blockNumber : conf.medianTimeBlocks; - // Get their 'time' value - // console.log('Times between ', blockNumber - blocksCount, blockNumber - 1); - let blocksBetween = yield dal.getBlocksBetween(blockNumber - blocksCount, blockNumber - 1); - let timeValues = _.pluck(blocksBetween, 'time'); - timeValues.sort(); - let sum = 0; - for (const timeValue of timeValues) { - sum += timeValue; - } - if (timeValues.length) { - return Math.floor(sum / timeValues.length); - } - else { - throw Error('No block found for MedianTime comparison'); - } - }); -} - -function getNewLinks (block) { - const newLinks = {}; - block.certifications.forEach(function(inlineCert){ - const cert = Certification.statics.fromInline(inlineCert); - newLinks[cert.to] = newLinks[cert.to] || []; - newLinks[cert.to].push(cert.from); - }); - return newLinks; -} - module.exports = rules; diff --git a/app/lib/rules/index.js b/app/lib/rules/index.js index f458d235b4d9b990c6318bd5e7aa918ac56eb189..2ca727c61c53339dac37200643bdba4083590260 100644 --- a/app/lib/rules/index.js +++ b/app/lib/rules/index.js @@ -29,17 +29,17 @@ rules.ALIAS = { yield rules.LOCAL.checkBlockSignature(block); yield rules.LOCAL.checkBlockTimes(block, conf); yield rules.LOCAL.checkIdentitiesSignature(block); - yield rules.LOCAL.checkIdentitiesUserIDConflict(block); - yield rules.LOCAL.checkIdentitiesPubkeyConflict(block); - yield rules.LOCAL.checkIdentitiesMatchJoin(block); - yield rules.LOCAL.checkRevokedNotInMemberships(block); - yield rules.LOCAL.checkRevokedUnicity(block); - yield rules.LOCAL.checkRevokedAreExcluded(block); + yield rules.LOCAL.checkIdentitiesUserIDConflict(block, conf); + yield rules.LOCAL.checkIdentitiesPubkeyConflict(block, conf); + yield rules.LOCAL.checkIdentitiesMatchJoin(block, conf); + yield rules.LOCAL.checkMembershipUnicity(block, conf); + yield rules.LOCAL.checkRevokedUnicity(block, conf); + yield rules.LOCAL.checkRevokedAreExcluded(block, conf); yield rules.LOCAL.checkMembershipsSignature(block); yield rules.LOCAL.checkPubkeyUnicity(block); - yield rules.LOCAL.checkCertificationOneByIssuer(block); - yield rules.LOCAL.checkCertificationUnicity(block); - yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block); + yield rules.LOCAL.checkCertificationOneByIssuer(block, conf); + yield rules.LOCAL.checkCertificationUnicity(block, conf); + yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block, conf); yield rules.LOCAL.checkTxVersion(block); yield rules.LOCAL.checkTxIssuers(block); yield rules.LOCAL.checkTxSources(block); @@ -57,102 +57,23 @@ rules.ALIAS = { yield rules.LOCAL.checkUnitBase(block); yield rules.LOCAL.checkBlockTimes(block, conf); yield rules.LOCAL.checkIdentitiesSignature(block); - yield rules.LOCAL.checkIdentitiesUserIDConflict(block); - yield rules.LOCAL.checkIdentitiesPubkeyConflict(block); - yield rules.LOCAL.checkIdentitiesMatchJoin(block); - yield rules.LOCAL.checkRevokedNotInMemberships(block); - yield rules.LOCAL.checkRevokedUnicity(block); - yield rules.LOCAL.checkRevokedAreExcluded(block); + yield rules.LOCAL.checkIdentitiesUserIDConflict(block, conf); + yield rules.LOCAL.checkIdentitiesPubkeyConflict(block, conf); + yield rules.LOCAL.checkIdentitiesMatchJoin(block, conf); + yield rules.LOCAL.checkMembershipUnicity(block, conf); + yield rules.LOCAL.checkRevokedUnicity(block, conf); + yield rules.LOCAL.checkRevokedAreExcluded(block, conf); yield rules.LOCAL.checkMembershipsSignature(block); yield rules.LOCAL.checkPubkeyUnicity(block); - yield rules.LOCAL.checkCertificationOneByIssuer(block); - yield rules.LOCAL.checkCertificationUnicity(block); - yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block); + yield rules.LOCAL.checkCertificationOneByIssuer(block, conf); + yield rules.LOCAL.checkCertificationUnicity(block, conf); + yield rules.LOCAL.checkCertificationIsntForLeaverOrExcluded(block, conf); yield rules.LOCAL.checkTxVersion(block); yield rules.LOCAL.checkTxIssuers(block); yield rules.LOCAL.checkTxSources(block); yield rules.LOCAL.checkTxRecipients(block); yield rules.LOCAL.checkTxAmounts(block); yield rules.LOCAL.checkTxSignature(block); - }), - - ALL_GLOBAL: (block, conf, dal) => co(function *() { - yield rules.GLOBAL.checkNumber(block, dal); - yield rules.GLOBAL.checkVersion(block, dal); - yield rules.GLOBAL.checkBlockLength(block, dal); - yield rules.GLOBAL.checkPreviousHash(block, dal); - yield rules.GLOBAL.checkPreviousIssuer(block, dal); - yield rules.GLOBAL.checkIssuerIsMember(block, dal); - yield rules.GLOBAL.checkIssuersFrame(block, conf, dal); - yield rules.GLOBAL.checkIssuersFrameVar(block, conf, dal); - yield rules.GLOBAL.checkDifferentIssuersCount(block, conf, dal); - yield rules.GLOBAL.checkTimes(block, conf, dal); - yield rules.GLOBAL.checkIdentityUnicity(block, conf, dal); - yield rules.GLOBAL.checkPubkeyUnicity(block, conf, dal); - yield rules.GLOBAL.checkIdentitiesAreWritable(block, conf, dal); - yield rules.GLOBAL.checkMembershipsAreWritable(block, conf, dal); - yield rules.GLOBAL.checkJoiners(block, conf, dal); - yield rules.GLOBAL.checkJoinersHaveEnoughCertifications(block, conf, dal); - yield rules.GLOBAL.checkJoinersAreNotOudistanced(block, conf, dal); - yield rules.GLOBAL.checkActives(block, conf, dal); - yield rules.GLOBAL.checkActivesAreNotOudistanced(block, conf, dal); - yield rules.GLOBAL.checkLeavers(block, conf, dal); - yield rules.GLOBAL.checkRevoked(block, conf, dal); - yield rules.GLOBAL.checkJoinersAreNotRevoked(block, conf, dal); - yield rules.GLOBAL.checkExcluded(block, conf, dal); - yield rules.GLOBAL.checkKickedMembersAreExcluded(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreWritable(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreMadeByMembers(block, dal); - yield rules.GLOBAL.checkCertificationsAreValid(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreMadeToMembers(block, dal); - yield rules.GLOBAL.checkCertificationsAreMadeToNonLeaver(block, dal); - yield rules.GLOBAL.checkCertificationsDelayIsRespected(block, conf, dal); - yield rules.GLOBAL.checkCertificationsPeriodIsRespected(block, conf, dal); - yield rules.GLOBAL.checkMembersCountIsGood(block, dal); - yield rules.GLOBAL.checkPoWMin(block, conf, dal); - yield rules.GLOBAL.checkProofOfWork(block, conf, dal); - yield rules.GLOBAL.checkUD(block, conf, dal); - yield rules.GLOBAL.checkTransactionsBlockStamp(block, conf, dal); - yield rules.GLOBAL.checkSourcesAvailability(block, conf, dal); - }), - - ALL_GLOBAL_WITHOUT_POW: (block, conf, dal) => co(function *() { - yield rules.GLOBAL.checkNumber(block, dal); - yield rules.GLOBAL.checkVersion(block, dal); - yield rules.GLOBAL.checkBlockLength(block, dal); - yield rules.GLOBAL.checkPreviousHash(block, dal); - yield rules.GLOBAL.checkPreviousIssuer(block, dal); - yield rules.GLOBAL.checkIssuerIsMember(block, dal); - yield rules.GLOBAL.checkIssuersFrame(block, conf, dal); - yield rules.GLOBAL.checkIssuersFrameVar(block, conf, dal); - yield rules.GLOBAL.checkDifferentIssuersCount(block, conf, dal); - yield rules.GLOBAL.checkTimes(block, conf, dal); - yield rules.GLOBAL.checkIdentityUnicity(block, conf, dal); - yield rules.GLOBAL.checkPubkeyUnicity(block, conf, dal); - yield rules.GLOBAL.checkIdentitiesAreWritable(block, conf, dal); - yield rules.GLOBAL.checkMembershipsAreWritable(block, conf, dal); - yield rules.GLOBAL.checkJoiners(block, conf, dal); - yield rules.GLOBAL.checkJoinersHaveEnoughCertifications(block, conf, dal); - yield rules.GLOBAL.checkJoinersAreNotOudistanced(block, conf, dal); - yield rules.GLOBAL.checkActives(block, conf, dal); - yield rules.GLOBAL.checkActivesAreNotOudistanced(block, conf, dal); - yield rules.GLOBAL.checkLeavers(block, conf, dal); - yield rules.GLOBAL.checkRevoked(block, conf, dal); - yield rules.GLOBAL.checkJoinersAreNotRevoked(block, conf, dal); - yield rules.GLOBAL.checkExcluded(block, conf, dal); - yield rules.GLOBAL.checkKickedMembersAreExcluded(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreWritable(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreMadeByMembers(block, dal); - yield rules.GLOBAL.checkCertificationsAreValid(block, conf, dal); - yield rules.GLOBAL.checkCertificationsAreMadeToMembers(block, dal); - yield rules.GLOBAL.checkCertificationsAreMadeToNonLeaver(block, dal); - yield rules.GLOBAL.checkCertificationsDelayIsRespected(block, conf, dal); - yield rules.GLOBAL.checkCertificationsPeriodIsRespected(block, conf, dal); - yield rules.GLOBAL.checkMembersCountIsGood(block, dal); - yield rules.GLOBAL.checkPoWMin(block, conf, dal); - yield rules.GLOBAL.checkUD(block, conf, dal); - yield rules.GLOBAL.checkTransactionsBlockStamp(block, conf, dal); - yield rules.GLOBAL.checkSourcesAvailability(block, conf, dal); }) }; @@ -160,8 +81,6 @@ rules.CHECK = { ASYNC: { ALL_LOCAL: checkLocal(rules.ALIAS.ALL_LOCAL), ALL_LOCAL_BUT_POW: checkLocal(rules.ALIAS.ALL_LOCAL_BUT_POW_AND_SIGNATURE), - ALL_GLOBAL: check(rules.ALIAS.ALL_GLOBAL), - ALL_GLOBAL_BUT_POW: check(rules.ALIAS.ALL_GLOBAL_WITHOUT_POW) } }; @@ -180,10 +99,10 @@ function checkLocal(contract) { } function check(contract) { - return (b, conf, dal, done) => { + return (b, conf, dal, bcContext, done) => { return co(function *() { const block = new Block(b); - yield contract(block, conf, dal); + yield contract(block, conf, dal, bcContext); done && done(); }) .catch((err) => { diff --git a/app/lib/rules/local_rules.js b/app/lib/rules/local_rules.js index 0e1dee584b57d763f1abf51db20ec48d34ed423e..7cf955c22e006546eb692c8ea0c4c6191a8f6498 100644 --- a/app/lib/rules/local_rules.js +++ b/app/lib/rules/local_rules.js @@ -1,7 +1,9 @@ "use strict"; const co = require('co'); +const _ = require('underscore'); const constants = require('../constants'); +const indexer = require('../dup/indexer'); const hashf = require('../ucp/hashf'); const keyring = require('../crypto/keyring'); const rawer = require('../ucp/rawer'); @@ -11,6 +13,8 @@ const Transaction = require('../entity/transaction'); let rules = {}; +// TODO: make a global variable 'index' instead of recomputing the index each time + rules.FUNCTIONS = { checkVersion: (block) => co(function*() { @@ -114,113 +118,71 @@ rules.FUNCTIONS = { return true; }), - checkIdentitiesUserIDConflict: (block) => co(function *() { - const uids = []; - let i = 0; - let conflict = false; - while (!conflict && i < block.identities.length) { - const uid = block.identities[i].split(':')[3]; - conflict = ~uids.indexOf(uid); - uids.push(uid); - i++; - } - if (conflict) { + checkIdentitiesUserIDConflict: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const creates = indexer.iindexCreate(index); + const uids = _.chain(creates).pluck('uid').uniq().value(); + if (creates.length !== uids.length) { throw Error('Block must not contain twice same identity uid'); } return true; }), - checkIdentitiesPubkeyConflict: (block) => co(function *() { - const pubkeys = []; - let i = 0; - let conflict = false; - while (!conflict && i < block.identities.length) { - const pubk = block.identities[i].split(':')[0]; - conflict = ~pubkeys.indexOf(pubk); - pubkeys.push(pubk); - i++; - } - if (conflict) { + checkIdentitiesPubkeyConflict: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const creates = indexer.iindexCreate(index); + const pubkeys = _.chain(creates).pluck('pub').uniq().value(); + if (creates.length !== pubkeys.length) { throw Error('Block must not contain twice same identity pubkey'); } return true; }), - checkIdentitiesMatchJoin: (block) => co(function *() { - // N.B.: this function does not test for potential duplicates in - // identities and/or joiners, this is another test responsibility - const pubkeys = []; - block.identities.forEach(function(inline){ - let sp = inline.split(':'); - let pubk = sp[0], ts = sp[2], uid = sp[3]; - pubkeys.push([pubk, uid, ts].join('-')); - }); - let matchCount = 0; - let i = 0; - while (i < block.joiners.length) { - let sp = block.joiners[i].split(':'); - let pubk = sp[0], ts = sp[3], uid = sp[4]; - let idty = [pubk, uid, ts].join('-'); - if (~pubkeys.indexOf(idty)) matchCount++; - i++; - } - let problem = matchCount != pubkeys.length; - if (problem) { - throw Error('Each identity must match a newcomer line with same userid and certts'); + checkIdentitiesMatchJoin: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const icreates = indexer.iindexCreate(index); + const mcreates = indexer.mindexCreate(index); + for (const icreate of icreates) { + const matching = _(mcreates).filter({ pub: icreate.pub }); + if (matching.length == 0) { + throw Error('Each identity must match a newcomer line with same userid and certts'); + } } return true; }), - checkRevokedAreExcluded: (block) => co(function *() { - // N.B.: this function does not test for potential duplicates in Revoked, - // this is another test responsability - const pubkeys = []; - block.revoked.forEach(function(inline){ - let sp = inline.split(':'); - let pubk = sp[0]; - pubkeys.push(pubk); - }); - let matchCount = 0; - let i = 0; - while (i < block.excluded.length) { - if (~pubkeys.indexOf(block.excluded[i])) matchCount++; - i++; - } - let problem = matchCount != pubkeys.length; - if (problem) { - throw Error('A revoked member must be excluded'); + checkRevokedAreExcluded: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const iindex = indexer.iindex(index); + const mindex = indexer.mindex(index); + const revocations = _.chain(mindex) + .filter((row) => row.op == constants.IDX_UPDATE && row.revoked_on !== null) + .pluck('pub') + .value(); + for (const pub of revocations) { + const exclusions = _(iindex).where({ op: constants.IDX_UPDATE, member: false }); + if (exclusions.length == 0) { + throw Error('A revoked member must be excluded'); + } } return true; }), - checkRevokedUnicity: (block) => co(function *() { - let pubkeys = []; - let conflict = false; - let i = 0; - while (!conflict && i < block.revoked.length) { - let pubk = block.revoked[i].split(':')[0]; - conflict = ~pubkeys.indexOf(pubk); - pubkeys.push(pubk); - i++; - } - if (conflict) { + checkRevokedUnicity: (block, conf) => co(function *() { + try { + yield rules.FUNCTIONS.checkMembershipUnicity(block, conf); + } catch (e) { throw Error('A single revocation per member is allowed'); } return true; }), - checkRevokedNotInMemberships: (block) => co(function *() { - let i = 0; - let conflict = false; - while (!conflict && i < block.revoked.length) { - let pubk = block.revoked[i].split(':')[0]; - conflict = existsPubkeyIn(pubk, block.joiners) - || existsPubkeyIn(pubk, block.actives) - || existsPubkeyIn(pubk, block.leavers); - i++; - } - if (conflict) { - throw Error('A revoked pubkey cannot have a membership in the same block'); + checkMembershipUnicity: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const mindex = indexer.mindex(index); + const pubkeys = _.chain(mindex).pluck('pub').uniq().value(); + if (pubkeys.length !== mindex.length) { + throw Error('Unicity constraint PUBLIC_KEY on MINDEX is not respected'); } return true; }), @@ -296,60 +258,40 @@ rules.FUNCTIONS = { return true; }), - checkCertificationOneByIssuer: (block) => co(function *() { - let conflict = false; + checkCertificationOneByIssuer: (block, conf) => co(function *() { if (block.number > 0) { - const issuers = []; - let i = 0; - while (!conflict && i < block.certifications.length) { - const issuer = block.certifications[i].split(':')[0]; - conflict = ~issuers.indexOf(issuer); - issuers.push(issuer); - i++; + const index = indexer.localIndex(block, conf); + const cindex = indexer.cindex(index); + const certFromA = _.uniq(cindex.map((row) => row.issuer)); + if (certFromA.length !== cindex.length) { + throw Error('Block cannot contain two certifications from same issuer'); } } - if (conflict) { - throw Error('Block cannot contain two certifications from same issuer'); - } return true; }), - checkCertificationUnicity: (block) => co(function *() { - const certs = []; - let i = 0; - let conflict = false; - while (!conflict && i < block.certifications.length) { - const cert = block.certifications[i].split(':').slice(0,2).join(':'); - conflict = ~certs.indexOf(cert); - certs.push(cert); - i++; - } - if (conflict) { + checkCertificationUnicity: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const cindex = indexer.cindex(index); + const certAtoB = _.uniq(cindex.map((row) => row.issuer + row.receiver)); + if (certAtoB.length !== cindex.length) { throw Error('Block cannot contain identical certifications (A -> B)'); } return true; }), - checkCertificationIsntForLeaverOrExcluded: (block) => co(function *() { - const pubkeys = []; - block.leavers.forEach(function(leaver){ - const pubk = leaver.split(':')[0]; - pubkeys.push(pubk); - }); - block.excluded.forEach(function(excluded){ - pubkeys.push(excluded); - }); - // Certifications - let conflict = false; - let i = 0; - while (!conflict && i < block.certifications.length) { - const sp = block.certifications[i].split(':'); - const pubkFrom = sp[0], pubkTo = sp[1]; - conflict = ~pubkeys.indexOf(pubkFrom) || ~pubkeys.indexOf(pubkTo); - i++; - } - if (conflict) { - throw Error('Block cannot contain certifications concerning leavers or excluded members'); + checkCertificationIsntForLeaverOrExcluded: (block, conf) => co(function *() { + const index = indexer.localIndex(block, conf); + const cindex = indexer.cindex(index); + const iindex = indexer.iindex(index); + const mindex = indexer.mindex(index); + const certified = cindex.map((row) => row.receiver); + for (const pub of certified) { + const exclusions = _(iindex).where({ op: constants.IDX_UPDATE, member: false, pub: pub }); + const leavers = _(mindex).where({ op: constants.IDX_UPDATE, leaving: true, pub: pub }); + if (exclusions.length > 0 || leavers.length > 0) { + throw Error('Block cannot contain certifications concerning leavers or excluded members'); + } } return true; }), @@ -392,26 +334,20 @@ rules.FUNCTIONS = { return true; }), - checkTxSources: (block) => co(function *() { + checkTxSources: (block, conf) => co(function *() { const txs = block.getTransactions(); - const sources = []; - let i = 0; - let existsIdenticalSource = false; - while (!existsIdenticalSource && i < txs.length) { - const tx = txs[i]; + for (const tx of txs) { if (!tx.inputs || tx.inputs.length == 0) { throw Error('A transaction must have at least 1 source'); } - tx.inputs.forEach(function (input) { - if (~sources.indexOf(input.raw)) { - existsIdenticalSource = true; - } else { - sources.push(input.raw); - } - }); - i++; } - if (existsIdenticalSource) { + const sindex = indexer.localSIndex(block); + const inputs = _.filter(sindex, (row) => row.op == constants.IDX_UPDATE).map((row) => [row.op, row.identifier, row.pos].join('-')); + if (inputs.length !== _.uniq(inputs).length) { + throw Error('It cannot exist 2 identical sources for transactions inside a given block'); + } + const outputs = _.filter(sindex, (row) => row.op == constants.IDX_CREATE).map((row) => [row.op, row.identifier, row.pos].join('-')); + if (outputs.length !== _.uniq(outputs).length) { throw Error('It cannot exist 2 identical sources for transactions inside a given block'); } return true; @@ -465,22 +401,11 @@ function maxAcceleration (block, conf) { } } -function existsPubkeyIn(pubk, memberships) { - let i = 0; - let conflict = false; - while (!conflict && i < memberships.length) { - let pubk2 = memberships[i].split(':')[0]; - conflict = pubk == pubk2; - i++; - } - return conflict; -} - function checkSingleMembershipSignature(ms) { return keyring.verify(ms.getRaw(), ms.signature, ms.issuer); } -function getSigResult(tx) { +function getSigResult(tx, a) { let sigResult = { sigs: {}, matching: true }; let json = { "version": tx.version, "currency": tx.currency, "blockstamp": tx.blockstamp, "locktime": tx.locktime, "inputs": [], "outputs": [], "issuers": tx.issuers, "signatures": [], "comment": tx.comment }; tx.inputs.forEach(function (input) { diff --git a/app/lib/sync.js b/app/lib/sync.js index af174d632e5979af7e9fa9c9847021aaf2e88b22..815674238d9690c4b37c51592b5f3b45b52b2637 100644 --- a/app/lib/sync.js +++ b/app/lib/sync.js @@ -7,10 +7,12 @@ const _ = require('underscore'); const moment = require('moment'); const contacter = require('./contacter'); const hashf = require('./ucp/hashf'); +const indexer = require('./dup/indexer'); const dos2unix = require('./system/dos2unix'); const logger = require('./logger')('sync'); const rawer = require('./ucp/rawer'); const constants = require('../lib/constants'); +const Block = require('../lib/entity/block'); const Peer = require('../lib/entity/peer'); const multimeter = require('multimeter'); const pulling = require('../lib/pulling'); @@ -163,12 +165,22 @@ function Synchroniser (server, host, port, conf, interactive) { logger.info('Downloading Blockchain...'); // We use cautious mode if it is asked, or not particulary asked but blockchain has been started - const cautious = (askedCautious === true || (askedCautious === undefined && localNumber >= 0)); + const cautious = (askedCautious === true || localNumber >= 0); const downloader = new P2PDownloader(localNumber, to, rCurrent.hash, CONST_MAX_SIMULTANEOUS_DOWNLOADS, peers, watcher); downloader.start(); let lastPullBlock = null; + + let bindex = []; + let iindex = []; + let mindex = []; + let cindex = []; + let sindex = []; + let currConf = {}; + let bindexSize = 0; + let allBlocks = []; + let dao = pulling.abstractDao({ // Get the local blockchain current block @@ -206,10 +218,103 @@ function Synchroniser (server, host, port, conf, interactive) { applyBranch: (blocks) => co(function *() { if (cautious) { for (const block of blocks) { + if (block.number == 0) { + yield BlockchainService.saveParametersForRootBlock(block); + currConf = Block.statics.getConf(block); + } yield dao.applyMainBranch(block); } } else { - yield server.BlockchainService.saveBlocksInMainBranch(blocks); + const ctx = BlockchainService.getContext(); + let blocksToSave = []; + + for (const block of blocks) { + allBlocks.push(block); + + if (block.number == 0) { + currConf = Block.statics.getConf(block); + } + + if (block.number != to) { + blocksToSave.push(block); + const index = indexer.localIndex(block, currConf); + const local_iindex = indexer.iindex(index); + const local_cindex = indexer.cindex(index); + iindex = iindex.concat(local_iindex); + cindex = cindex.concat(local_cindex); + sindex = sindex.concat(indexer.sindex(index)); + mindex = mindex.concat(indexer.mindex(index)); + const HEAD = yield indexer.quickCompleteGlobalScope(block, currConf, bindex, iindex, mindex, cindex, sindex, { + getBlock: (number) => { + return Promise.resolve(allBlocks[number - 1]); + }, + getBlockByBlockstamp: (blockstamp) => { + return Promise.resolve(allBlocks[parseInt(blockstamp) - 1]); + } + }); + bindex.push(HEAD); + + yield ctx.createNewcomers(local_iindex); + + if (block.dividend + || block.joiners.length + || block.actives.length + || block.revoked.length + || block.excluded.length + || block.certifications.length) { + // Flush the INDEX (not bindex, which is particular) + yield dal.mindexDAL.insertBatch(mindex); + yield dal.iindexDAL.insertBatch(iindex); + yield dal.sindexDAL.insertBatch(sindex); + yield dal.cindexDAL.insertBatch(cindex); + mindex = []; + iindex = []; + cindex = []; + sindex = yield indexer.ruleIndexGenDividend(HEAD, dal); + + // Create/Update nodes in wotb + yield ctx.updateMembers(block); + + // --> Update links + yield dal.updateWotbLinks(local_cindex); + } + + // Trim the bindex + bindexSize = [ + block.issuersCount, + block.issuersFrame, + conf.medianTimeBlocks, + conf.dtDiffEval, + CONST_BLOCKS_CHUNK + ].reduce((max, value) => { + return Math.max(max, value); + }, 0); + + if (bindexSize && bindex.length >= 2 * bindexSize) { + // We trim it, not necessary to store it all (we already store the full blocks) + bindex.splice(0, bindexSize); + } + } else { + + if (blocksToSave.length) { + yield server.BlockchainService.saveBlocksInMainBranch(blocksToSave); + } + blocksToSave = []; + + // Save the INDEX + yield dal.bindexDAL.insertBatch(bindex); + yield dal.mindexDAL.insertBatch(mindex); + yield dal.iindexDAL.insertBatch(iindex); + yield dal.sindexDAL.insertBatch(sindex); + yield dal.cindexDAL.insertBatch(cindex); + + // Last block: cautious mode to trigger all the INDEX expiry mechanisms + yield dao.applyMainBranch(block); + } + } + if (blocksToSave.length) { + yield server.BlockchainService.saveBlocksInMainBranch(blocksToSave); + } } lastPullBlock = blocks[blocks.length - 1]; watcher.appliedPercent(Math.floor(blocks[blocks.length - 1].number / to * 100)); diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js index 5dfed2d9d71b998990688ea1c54d3ba4bdbdb3fd..d2d7d0badaba1e08a99c33fbae173926522a83cb 100644 --- a/app/service/BlockchainService.js +++ b/app/service/BlockchainService.js @@ -33,6 +33,8 @@ function BlockchainService (server) { const generator = blockGenerator(mainContext, prover); let conf, dal, keyPair, logger, selfPubkey; + this.getContext = () => mainContext; + this.setConfDAL = (newConf, newDAL, newKeyPair) => { dal = newDAL; conf = newConf; @@ -268,7 +270,7 @@ function BlockchainService (server) { /** * Generates next block, finding newcomers, renewers, leavers, certs, transactions, etc. */ - this.generateNext = () => generator.nextBlock(); + this.generateNext = (params) => generator.nextBlock(params); this.requirementsOfIdentities = (identities) => co(function *() { let all = []; @@ -308,9 +310,11 @@ function BlockchainService (server) { certs = yield that.getValidCerts(pubkey, newCerts); outdistanced = yield rules.HELPERS.isOver3Hops(currentVersion, pubkey, newLinks, someNewcomers, current, conf, dal); // Expiration of current membershship - if (join.identity.currentMSN >= 0) { + const currentMembership = yield dal.mindexDAL.getReducedMS(pubkey); + const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1; + if (currentMSN >= 0) { if (join.identity.member) { - const msBlock = yield dal.getBlock(join.identity.currentMSN); + const msBlock = yield dal.getBlock(currentMSN); if (msBlock && msBlock.medianTime) { // special case for block #0 expiresMS = Math.max(0, (msBlock.medianTime + conf.msValidity - currentTime)); } @@ -360,7 +364,7 @@ function BlockchainService (server) { this.getValidCerts = (newcomer, newCerts) => co(function *() { const links = yield dal.getValidLinksTo(newcomer); - const certsFromLinks = links.map((lnk) => { return { from: lnk.source, to: lnk.target, timestamp: lnk.timestamp }; }); + const certsFromLinks = links.map((lnk) => { return { from: lnk.issuer, to: lnk.receiver, timestamp: lnk.expires_on - conf.sigValidity }; }); const certsFromCerts = []; const certs = newCerts[newcomer] || []; for (const cert of certs) { @@ -409,64 +413,11 @@ function BlockchainService (server) { } return block; }); - // Insert a bunch of blocks - const lastPrevious = blocks[0].number == 0 ? null : yield dal.getBlock(blocks[0].number - 1); - const dividends = []; - for (let i = 0; i < blocks.length; i++) { - const previous = i > 0 ? blocks[i - 1] : lastPrevious; - const block = blocks[i]; - block.len = Block.statics.getLen(block); + for (const block of blocks) { block.fork = false; - // Monetary mass & UD Time recording before inserting elements - block.monetaryMass = (previous && previous.monetaryMass) || 0; - block.unitbase = (block.dividend && block.unitbase) || (previous && previous.unitbase) || 0; - block.dividend = block.dividend || null; - // UD Time update - const previousBlock = i > 0 ? blocks[i - 1] : lastPrevious; - if (block.number == 0) { - block.UDTime = block.medianTime; // Root = first UD time - } - else if (block.dividend) { - block.UDTime = conf.dt + previousBlock.UDTime; - block.monetaryMass += block.dividend * Math.pow(10, block.unitbase || 0) * block.membersCount; - } else { - block.UDTime = previousBlock.UDTime; - } - yield mainContext.updateMembers(block); - - // Dividends - if (block.dividend) { - // Get the members at THAT moment (only them should have the UD) - let idties = yield dal.getMembers(); - for (const idty of idties) { - dividends.push({ - 'pubkey': idty.pubkey, - 'identifier': idty.pubkey, - 'noffset': block.number, - 'type': 'D', - 'number': block.number, - 'time': block.medianTime, - 'fingerprint': block.hash, - 'block_hash': block.hash, - 'amount': block.dividend, - 'base': block.unitbase, - 'consumed': false, - 'toConsume': false, - 'conditions': 'SIG(' + idty.pubkey + ')' // Only this pubkey can unlock its UD - }); - } - } } // Transactions recording yield mainContext.updateTransactionsForBlocks(blocks, getBlockByNumberAndHash); - // Create certifications - yield mainContext.updateMembershipsForBlocks(blocks); - // Create certifications - yield mainContext.updateLinksForBlocks(blocks, getBlock); - // Create certifications - yield mainContext.updateCertificationsForBlocks(blocks); - // Create / Update sources - yield mainContext.updateTransactionSourcesForBlocks(blocks, dividends); logger.debug(blocks[0].number); yield dal.blockDAL.saveBunch(blocks); yield pushStatsForBlocks(blocks); @@ -494,15 +445,6 @@ function BlockchainService (server) { return dal.pushStats(stats); } - this.getCertificationsExludingBlock = () => co(function*() { - try { - const current = yield dal.getCurrentBlockOrNull(); - return yield dal.getCertificationExcludingBlock(current, conf.sigValidity); - } catch (err) { - return { number: -1 }; - } - }); - this.blocksBetween = (from, count) => co(function *() { if (count > 5000) { throw 'Count is too high'; diff --git a/app/service/IdentityService.js b/app/service/IdentityService.js index 2e75582471ac88ee23c7163e49713f05e7df8c97..4d0d5c7e5f2bc9812c711c78e684bebb2a08775f 100644 --- a/app/service/IdentityService.js +++ b/app/service/IdentityService.js @@ -91,6 +91,15 @@ function IdentityService () { throw constants.ERRORS.UID_ALREADY_USED; } const current = yield dal.getCurrentBlockOrNull(); + if (idty.buid == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855' && current) { + throw constants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK; + } else if (current) { + let basedBlock = yield dal.getBlockByBlockstamp(idty.buid); + if (!basedBlock) { + throw constants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK; + } + idty.expires_on = basedBlock.medianTime + conf.idtyWindow; + } yield rules.GLOBAL.checkIdentitiesAreWritable({ identities: [idty.inline()], version: (current && current.version) || constants.BLOCK_GENERATED_VERSION }, conf, dal); idty = new Identity(idty); if (byAbsorption === BY_ABSORPTION) { @@ -140,6 +149,8 @@ function IdentityService () { number: 0, hash: 'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855' }; + } else { + cert.expires_on = basedBlock.medianTime + conf.sigWindow; } cert.block_hash = basedBlock.hash; const mCert = new Certification({ @@ -148,7 +159,8 @@ function IdentityService () { block_number: cert.block_number, block_hash: cert.block_hash, target: targetHash, - to: idty.pubkey + to: idty.pubkey, + expires_on: cert.expires_on }); let existingCert = yield dal.existsCert(mCert); if (!existingCert) { @@ -185,7 +197,7 @@ function IdentityService () { else if (existing.revocation_sig) { throw 'Revocation already registered'; } else { - yield dal.setRevocating(obj.hash, revoc.revocation); + yield dal.setRevocating(existing, revoc.revocation); return jsonResultTrue(); } } diff --git a/app/service/MembershipService.js b/app/service/MembershipService.js index 04ad95c41b39ab9e23a2860b5281805f27903d03..3eb0ccb488da0e550fcc2df9d2c458eada1f3911 100644 --- a/app/service/MembershipService.js +++ b/app/service/MembershipService.js @@ -35,9 +35,12 @@ function MembershipService () { throw constants.ERRORS.WRONG_SIGNATURE_MEMBERSHIP; } // Get already existing Membership with same parameters - const found = yield dal.getMembershipForHashAndIssuer(entry); - if (found) { + const mostRecentNumber = yield dal.getMostRecentMembershipNumberForIssuer(entry.issuer); + const thisNumber = parseInt(entry.block); + if (mostRecentNumber == thisNumber) { throw constants.ERRORS.ALREADY_RECEIVED_MEMBERSHIP; + } else if (mostRecentNumber > thisNumber) { + throw constants.ERRORS.A_MORE_RECENT_MEMBERSHIP_EXISTS; } const isMember = yield dal.isMember(entry.issuer); const isJoin = entry.membership == 'IN'; @@ -46,7 +49,10 @@ function MembershipService () { throw constants.ERRORS.MEMBERSHIP_A_NON_MEMBER_CANNOT_LEAVE; } const current = yield dal.getCurrentBlockOrNull(); - yield rules.HELPERS.checkMembershipBlock(entry, current, conf, dal); + const basedBlock = yield rules.HELPERS.checkMembershipBlock(entry, current, conf, dal); + if (basedBlock) { + entry.expires_on = basedBlock.medianTime + conf.msWindow; + } entry.pubkey = entry.issuer; if (!(yield dal.msDAL.sandbox.acceptNewSandBoxEntry(entry, conf.pair && conf.pair.pub))) { throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL; diff --git a/doc/Protocol.md b/doc/Protocol.md index de8701262625e4a2ee328be69e1f33ce4af48ff2..4091e587c437136728396dbeca19800087161e8f 100644 --- a/doc/Protocol.md +++ b/doc/Protocol.md @@ -1,4 +1,4 @@ -# UCP - Duniter Protocol +# DUP - Duniter Protocol > This document is still regularly updated (as of August 2016) @@ -81,7 +81,7 @@ The block ID of the block#433 is `433`. Its UID *might be* `433-FB11681FC1B3E36C A valid currency name is composed of alphanumeric characters, spaces, `-` or `_` and has a length of 2 to 50 characters. -#### Dates +#### Datesœ For any document using a date field, targeted date is to be understood as **UTC+0** reference. @@ -808,6 +808,17 @@ Excluded | Exluded members' public key | Alwa Transactions | A list of compact transactions | Always InnerHash | The hash value of the block's inner content | Always Nonce | An arbitrary nonce value | Always +BlockHash | Hash from `InnerHash: ` to `SIGNATURE` | Virtual +BlockSize | Hash from `InnerHash: ` to `SIGNATURE` | Virtual + +##### BlockSize + +The block size is defined as the number of lines in multiline fields (`Identities`, `Joiners`, `Actives`, `Leavers`, `Revoked`, `Certifications`, `Transactions`) **except** `Excluded` field. + +For example: + +* 1 new identity + 1 joiner + 2 certifications = 4 lines sized block +* 1 new identity + 1 joiner + 2 certifications + 5 lines transaction = 9 lines sized block #### Coherence @@ -959,6 +970,7 @@ avgGenTime | The average time for writing 1 block (wished time) dtDiffEval | The number of blocks required to evaluate again `PoWMin` value blocksRot | The number of previous blocks to check for personalized difficulty percentRot | The percent of previous issuers to reach for personalized difficulty +txWindow | `= 3600 * 24 * 7`. Maximum delay a transaction can wait before being expired for non-writing. ### Computed variables @@ -967,9 +979,9 @@ Variable | Meaning members | Synonym of `members(t = now)`, `wot(t)`, `community(t)` targeting the keys whose last active (non-expired) membership is either in `Joiners` or `Actives`. maxGenTime | `= CEIL(avgGenTime * 1.189)` minGenTime | `= FLOOR(avgGenTime / 1.189)` +minSpeed | 1 / maxGenTime +maxSpeed | 1 / minGenTime maxAcceleration | `= CEIL(maxGenTime * medianTimeBlocks)` -dSen | `= CEIL(membersCount ^ (1 / stepMax))` -sentries | Members with at least `dSen` active links *from* them ## Processing @@ -983,7 +995,11 @@ Local validation verifies the coherence of a well-formatted block, without any o ##### Version and Number -If `Number` is `0`, then `Version` must be `2`. +Rule: + + HEAD.version >= 2 + AND + HEAD.version <= 6 ##### InnerHash @@ -995,7 +1011,7 @@ If `Number` is `0`, then `Version` must be `2`. ##### Proof of work -To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must start with a specific number of zeros. Locally, this hash must start with at least `NB_ZEROS` zeros: +To be valid, the BlockHash must start with a specific number of zeros. Locally, this hash must start with at least `NB_ZEROS` zeros: REMAINDER = PowMin % 16 NB_ZEROS = (PowMin - REMAINDER) / 16 @@ -1015,6 +1031,10 @@ To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must * `Parameters` must be present if the value of the `Number` field is equal to `0`. * `Parameters` must not be present if the value of the `Number` field is greater than `0`. +##### Universal Dividend + +If HEAD.number == 0, HEAD.dividend must equal `null`. + ##### UnitBase * Block V2: @@ -1029,52 +1049,260 @@ To be valid, a block proof-of-work (hash from `InnerHash: ` to `SIGNATURE`) must ##### Dates -* A block must have its `Time` field be between [`MedianTime` ; `MedianTime` + `accelerationMax`]. +* A block must have its `Time` field be between [`MedianTime` ; `MedianTime` + `maxAcceleration`]. * Root block's `Time` & `MedianTime` must be equal. ##### Identities -* A block cannot contain identities whose signature does not match the identity's content -* A block cannot have two or more identities sharing a same `USER_ID`. -* A block cannot have two or more identities sharing a same `PUBLIC_KEY`. -* Each identity of a block must match a `Joiners` line matching the same `PUBLIC_KEY` +A block cannot contain identities whose signature does not match the identity's content ##### Memberships (Joiners, Actives, Leavers) -* A block cannot contain memberships whose signature does not match membership's content +A block cannot contain memberships whose signature does not match the membership's content -##### Members changes (Joiners, Actives, Leavers, Excluded) +##### Revoked -* A block cannot contain more than 1 occurrence of a same `PUBLIC_KEY` in `Joiners`, `Actives`, `Leavers` and `Excluded` field as a whole. In other words, a given `PUBLIC_KEY` present in `Joiners` cannot be present in `Joiners` a second time, neither can it be present in `Actives`, `Leavers` or `Excluded`. +A block cannot contain revocations whose signature does not match the revocation's content + +##### Transactions + +* A transaction in compact format cannot measure more than 100 lines +* A transaction must have at least 1 source +* A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count. +* A transaction **must** have signatures matching its content **for each issuer** +* A transaction's version: + * must be the same as its including block if the block's `Version` is `<= 3` + * must be equal to `3` if the block's `Version` is `> 3` +* Signatures count must be the same as issuers count +* Signatures are ordered by issuer +* Signatures are made over the transaction's content, signatures excepted + +##### INDEX GENERATION + +##### Identities + +Each identity produces 2 new entries: + + IINDEX ( + op = 'CREATE' + uid = USER_ID + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + member = true + wasMember = true + kick = false + ) + + MINDEX ( + op = 'CREATE' + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + expired_on = 0 + expires_on = MedianTime + msValidity + revokes_on = MedianTime + msValidity*2 + type = 'JOIN' + revoked_on = null + leaving = false + ) + +##### Joiners + +Each join whose `PUBLIC_KEY` **does not match** a local MINDEX `CREATE, PUBLIC_KEY` produces 2 new entries: + + IINDEX ( + op = 'UPDATE' + uid = null + pub = PUBLIC_KEY + created_on = null + written_on = BLOCKSTAMP + member = true + wasMember = null + kick = null + ) + + MINDEX ( + op = 'UPDATE' + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + expired_on = 0 + expires_on = MedianTime + msValidity + revokes_on = MedianTime + msValidity*2 + type = 'JOIN' + revoked_on = null + leaving = null + ) + +##### Actives + +Each active produces 1 new entry: + + MINDEX ( + op = 'UPDATE' + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + expires_on = MedianTime + msValidity + revokes_on = MedianTime + msValidity*2 + type = 'RENEW' + revoked_on = null + leaving = null + ) + +##### Leavers + +Each leaver produces 1 new entry: + + MINDEX ( + op = 'UPDATE' + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + type = 'LEAVE' + expires_on = null + revokes_on = null + revoked_on = null + leaving = true + ) ##### Revoked -* Each `PUBLIC_KEY` under `Revoked` field **must not** be present under `Joiners`, `Actives` or `Leavers` fields. -* A block cannot contain more than 1 occurrence of a same `PUBLIC_KEY` in `Revoked`. +Each revocation produces 1 new entry: + + MINDEX ( + op = 'UPDATE' + pub = PUBLIC_KEY + created_on = BLOCK_UID + written_on = BLOCKSTAMP + type = 'REV' + expires_on = null + revokes_on = null + revoked_on = BLOCKSTAMP + revocation = REVOCATION_SIG + leaving = false + ) ##### Excluded -* Each `PUBLIC_KEY` under `Revoked` field **must** be present under `Excluded` field. +Each exclusion produces 1 new entry: + + IINDEX ( + op = 'UPDATE' + uid = null + pub = PUBLIC_KEY + created_on = null + written_on = BLOCKSTAMP + member = false + wasMember = null + kick = false + ) ##### Certifications -* A block cannot have two certifications from a same `PUBKEY_FROM`, unless it is block#0. -* A block cannot have two identical certifications (same `PUBKEY_FROM` and same `PUBKEY_TO` for the two certifications) -* A block cannot have certifications for public keys present in either `Excluded` or `Leavers` fields. +Each certification produces 1 new entry: -##### Transactions + CINDEX ( + op = 'CREATE' + issuer = PUBKEY_FROM + receiver = PUBKEY_TO + created_on = BLOCK_ID + written_on = BLOCKSTAMP + sig = SIGNATURE + expires_on = MedianTime + sigValidity + chainable_on = MedianTime + sigPeriod + expired_on = 0 + ) -* A transaction in compact format cannot measure more than 100 lines -* A transaction must have at least 1 issuer, 1 source and 1 recipient -* For each issuer line, starting from line # `0`, there must be a source with an `INDEX` value equal to this line# -* A transaction cannot have 2 identical inputs -* A transaction cannot have 2 identical outputs -* A transaction cannot have `SIG(INDEX)` unlocks with `INDEX >= ` issuers count. -* A transaction **must** have signatures matching its content for each issuer -* A transaction's version: - * must be the same as its including block if the block's `Version` is `<= 3` - * must be equal to `3` if the block's `Version` is `> 3` -* There cannot be 2 transactions with the same source +##### Sources + +Each transaction input produces 1 new entry: + + SINDEX ( + op = 'UPDATE' + tx = TRANSACTION_HASH + identifier = INPUT_IDENTIFIER + pos = INPUT_INDEX + created_on = TX_BLOCKSTAMP + written_on = BLOCKSTAMP + amount = INPUT_AMOUNT + base = INPUT_BASE + conditions = null + consumed = true + ) + +Each transaction output produces 1 new entry: + + SINDEX ( + op = 'CREATE' + tx = TRANSACTION_HASH + identifier = TRANSACTION_HASH + pos = OUTPUT_INDEX_IN_TRANSACTION + written_on = BLOCKSTAMP + written_time = MedianTime + amount = OUTPUT_AMOUNT + base = OUTPUT_BASE + locktime = LOCKTIME + conditions = OUTPUT_CONDITIONS + consumed = false + ) + +##### INDEX RULES + +###### UserID and PublicKey unicity + +* The local IINDEX has a unicity constraint on `USER_ID`. +* The local IINDEX has a unicity constraint on `PUBLIC_KEY`. +* Each local IINDEX `op = 'CREATE'` operation must match a single local MINDEX `op = 'CREATE', pub = PUBLIC_KEY` operation. + +> Functionally: UserID and public key must be unique in a block, an each new identity must have an opt-in document attached. + +###### Membership unicity + +* The local MINDEX has a unicity constraint on `PUBLIC_KEY` + +> Functionally: a user has only 1 status change allowed per block. + +###### Revocation implies exclusion + +* Each local MINDEX ̀`op = 'UPDATE', revoked_on = BLOCKSTAMP` operations must match a single local IINDEX `op = 'UPDATE', pub = PUBLIC_KEY, member = false` operation. + +> Functionally: a revoked member must be immediately excluded. + +###### Certifications + +* The local CINDEX has a unicity constraint on `PUBKEY_FROM, PUBKEY_TO` +* The local CINDEX has a unicity constraint on `PUBKEY_FROM`, except for block#0 +* The local CINDEX must not match a MINDEX operation on `PUBLIC_KEY = PUBKEY_FROM, member = false` or `PUBLIC_KEY = PUBKEY_FROM, leaving = true` + +> Functionally: +> +> * a block cannot have 2 identical certifications (A -> B) +> * a block cannot have 2 certifications from a same public key, except in block#0 +> * a block cannot have a certification to a leaver or an excluded + +###### Sources + +* The local SINDEX has a unicity constraint on `UPDATE, IDENTIFIER, POS` +* The local SINDEX has a unicity constraint on `CREATE, IDENTIFIER, POS` + +> Functionally: +> * a same source cannot be consumed twice by the block +> * a same output cannot be produced twice by block +> +> But a source can be both created and consumed in the same block, so a *chain of transactions* can be stored at once. + +##### Double-spending control + +Definitions: + +For each SINDEX unique `tx`: + +* **inputs** are the SINDEX row matching `UPDATE, tx` +* **outputs** are the SINDEX row matching `CREATE, tx` + +> Functionally: we gather the sources for each transaction, in order to check them. ###### CommonBase @@ -1141,373 +1369,1118 @@ TRUE * if `BaseDelta > 0`, then it must be inferior or equal to the sum of all preceding `BaseDelta` * *Rule*: The sum of all inputs in `CommonBase` must equal the sum of all outputs in `CommonBase` -> Consequence: we cannot create money nor lose money through transactions. We can only transfer coins we own. - -###### About signatures - -* Signatures count must be the same as issuers count -* Signatures are ordered by issuer -* Signatures are made over the transaction's content, signatures excepted +> Functionally: we cannot create nor lose money through transactions. We can only transfer coins we own. #### Global Global validation verifies the coherence of a locally-validated block, in the context of the whole blockchain, including the block. -##### Definitions +##### INDEX GENERATION -###### Block size +###### Definitions -The block size is defined as the number of lines in multiline fields (`Identities`, `Joiners`, `Actives`, `Leavers`, `Revoked`, `Certifications`, `Transactions`) **except** `Excluded` field. +Here are some references that will be used in next sections. -For example: +BINDEX references: -* 1 new identity + 1 joiner + 2 certifications = 4 lines sized block -* 1 new identity + 1 joiner + 2 certifications + 5 lines transaction = 9 lines sized block +* *HEAD*: the BINDEX top entry (generated for incoming block, precisely) +* *HEAD~1*: the BINDEX 1<sup>st</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - 1`) +* *HEAD~n*: the BINDEX n<sup>th</sup> entry before HEAD (= entry where `ENTRY.number = HEAD.number - n`) +* *HEAD~n[field=value, ...]*: the BINDEX entry at *HEAD~n* if it fulfills the condition, null otherwise +* *HEAD~n..m[field=value, ...]*: the BINDEX entries between *HEAD~n* and *HEAD~m*, included, where each entry fulfills the condition +* *HEAD~n.property*: get a BINDEX entry property. Ex.: `HEAD~1.hash` looks at the hash of the entry preceding HEAD. +* *(HEAD~n..m).property*: get all the values of *property* in BINDEX for entries between *HEAD~n* and *HEAD~m*, included. +* *(HEAD~<n>..<m>).property*: same, but <n> and <m> are variables to be computed -The maximum size of a block `MAX_BLOCK_SIZE` is defined by: +Function references: -`MAX_BLOCK_SIZE = MAX(500 ; CEIL(1.10 * AVERAGE_BLOCK_SIZE))` +* *COUNT* returns the number of values in a list of values +* *AVG* computes the average value in a list of values, floor rounded. +* *MEDIAN* computes the median value in a list of values +* *MAX* computes the maximum value in a list of values -Where: `AVERAGE_BLOCK_SIZE` equals to the average block size of the `DifferentIssuersCount` of previous blocks. +> If values count is even, the median is computed over the 2 centered values by an arithmetical median on them, ceil rounded. -###### Block time +* *UNIQ* returns a list of the unique values in a list of values +* *INTEGER_PART* return the integer part of a number +* *FIRST* return the first element in a list of values matching the given condition +* *REDUCE* merges a set of elements into a single one, by extending the non-null properties from each record into the resulting record. +* *REDUCE_BY* merges a set of elements into a new set, where each new element is the reduction of the first set sharing a given key. -Block time is a special discrete time defined by the blocks themselves, where unit is *a block*, and values are *block number + fingerprint*. +> If there is no elements, all its properties are `null`. -So, refering to t<sub>block</sub> = 0-2B7A158B9FD052164005ED5B491699644A846CE2 is valid only if there exists a block#0 in the blockchain whose hash equals 2B7A158B9FD052164005ED5B491699644A846CE2. +* *NUMBER* get the number part of blockstamp +* *HASH* get the hash part of blockstamp -###### UD time +###### HEAD -UD time is a special discrete time defined by the UDs written in the blockchain where unit is a *UD*. +The block produces 1 new entry: -Refering to UD(t = 1) means UD#1, and refers to the *first UD* written in the blockchain. + BINDEX ( + version = Version + size = BlockSize + hash = BlockHash + issuer = Issuer + time = Time + number = null + currency = null + previousHash = null + previousIssuer = null + membersCount = null + issuersCount = null + issuersFrame = null + issuersFrameVar = null + issuerDiff = null + avgBlockSize = null + medianTime = null + dividend = null + mass = null + unitBase = null + powMin = PowMin + udTime = null + diffTime = null + speed = null + ) + +This entry will be refered to as *HEAD* in the following sections, and is added on the top of the BINDEX. -> UD(t = 0) means UD#0 which does not exist. However, UD#0 is a currency parameter noted **[ud0]**. +###### BR_G01 - HEAD.number -###### Calendar time +If HEAD~1 is defined: -Calendar time is the one provided by the blocks under `MedianTime` field. This time is discrete and the unit is seconds. + HEAD.number = HEAD~1.number + 1 -> *Current time* is to be understood as the last block calendar time written in the blockchain. +Else: -###### Certification time + HEAD.number = 0 + +###### BR_G02 - HEAD.previousHash -When making a certification, `BLOCK_ID` is a reference to *block time*. +If `HEAD.number > 0`: -###### Membership time + HEAD.previousHash = HEAD~1.hash -When making a membership, `NUMBER` is a reference to *block time*. +Else: -###### Certification & Membership age + HEAD.previousHash = null + +###### BR_G99 - HEAD.currency -Age is defined as the number of seconds between the certification's or membership's *block time* and *current time*: +If `HEAD.number > 0`: - AGE = current_time - block_time + HEAD.currency = HEAD~1.currency -###### Identity writability +Else: -An identity is to be considered *non-writable* if its age is less than or equal to `[idtyWindow]`: + HEAD.currency = null + +###### BR_G03 - HEAD.previousIssuer - VALID = AGE <= [idtyWindow] - EXPIRED = AGE > [idtyWindow] +If `HEAD.number > 0`: -###### Membership writability + HEAD.previousIssuer = HEAD~1.issuer -A membership is to be considered *non-writable* if its age is less than or equal to `[msWindow]`: +Else: - VALID = AGE <= [msWindow] - EXPIRED = AGE > [msWindow] + HEAD.previousIssuer = null + +###### BR_G100 - HEAD.issuerIsMember -###### Certification writability +If `HEAD.number > 0`: -A certification is to be considered *non-writable* if its age is less than or equal to `[sigWindow]`: + HEAD.issuerIsMember = REDUCE(GLOBAL_IINDEX[pub=HEAD.issuer]).member - VALID = AGE <= [sigWindow] - EXPIRED = AGE > [sigWindow] +Else: -###### Certification validity + HEAD.issuerIsMember = REDUCE(LOCAL_IINDEX[pub=HEAD.issuer]).member -A certification is to be considered *valid* if its age is less than or equal to `[sigValidity]`: +###### BR_G04 - HEAD.issuersCount + +If `HEAD.number == 0`: - VALID = AGE <= [sigValidity] - EXPIRED = AGE > [sigValidity] + HEAD.issuersCount = 0 + +Else if `HEAD~1.version > 2`: -###### Certification activity + HEAD.issuersCount = COUNT(UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer)) + +Else: -A certification is to be considered *active* if it is both written in the blockchain and *valid* (equivalent to not expired). + HEAD.issuersCount = COUNT(UNIQ((HEAD~1..40).issuer)) -###### Certification stock +###### BR_G05 - HEAD.issuersFrame -The stock of certification is defined per member and reflects the number of *active* certifications (i.e. not expired): +If `HEAD.number == 0`: - STOCK = COUNT(active_certifications) + HEAD.issuersFrame = 1 + +Else if `HEAD~1.version == 2`: -###### Membership validity + HEAD.issuersFrame = 40 + +Else if `HEAD~1.issuersFrameVar > 0`: -A membership is to be considered valid if its age is less than or equal to `[msValidity]`: + HEAD.issuersFrame = HEAD~1.issuersFrame + 1 + +Else if `HEAD~1.issuersFrameVar < 0`: - VALID = AGE <= [msValidity] - EXPIRED = AGE > [msValidity] + HEAD.issuersFrame = HEAD~1.issuersFrame - 1 + +Else: -###### Membership activity + HEAD.issuersFrame = HEAD~1.issuersFrame -A membership is to be considered *active* if it is both written in the blockchain and *valid* (i.e. not expired). +###### BR_G06 - HEAD.issuersFrameVar -###### Certification chaining +If `HEAD.number == 0` OR `HEAD~1.version == 2`: -A written certification is to be considered chainable if: + HEAD.issuersFrameVar = 0 + +Else if `HEAD~1.issuersFrameVar > 0`: -* its age is greater or equal to `[sigPeriod]`: -* the number of active certifications is lower than `[sigStock]`: + HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) - 1 + +Else if `HEAD~1.issuersFrameVar < 0`: - CHAINABLE = AGE >= [sigPeriod] && STOCK < [sigStock] + HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) + 1 + +Else: -###### Member + HEAD.issuersFrameVar = HEAD~1.issuersFrameVar + 5*(HEAD.issuersCount - HEAD~1.issuersCount) -A member is a `PUBLIC_KEY` matching a valid `Identity` whose last occurrence in the blockchain is either `Joiners`, `Actives` or `Leavers` **and is not expired**. -A `PUBLIC_KEY` whose last occurrence in the blockchain is `Leavers` or `Excluded`, or has no occurrence in the blockchain **is not** a member. +###### BR_G07 - HEAD.avgBlockSize -###### Revocation + HEAD.avgBlockSize = AVG((HEAD~1..<HEAD.issuersCount>).size) -An identity is considered *revoked* if either: +###### BR_G08 - HEAD.medianTime -* the age of its last `IN` membership is `>= 2 x [msValidity]` (implicit revocation) -* there exists a block in which the identity is found under `Revoked` field (explicit revocation) +If `HEAD.number > 0`: -##### Number + HEAD.medianTime = MEDIAN((HEAD~1..<MIN(medianTimeBlocks, HEAD.number)>).time) -* A block's `Number` must be exactly equal to previous block + 1. -* If the blockchain is empty, `Number` must be `0` . +Else: -##### Version + HEAD.medianTime = HEAD.time -`Version` must be between `[2, 5]`. Also, `Version` of incoming block must be equal to `Version` or `Version + 1` of current block. +###### BR_G09 - HEAD.diffNumber -##### PoWMin +If `HEAD.number == 0`: -###### Définitions -* `speedRange = MIN(dtDiffEval, incomingNumber)` -* `speed = speedRange / (medianTime(incomingNumber) - medianTime(incomingNumber - speedRange))` -* `maxSpeed = 1/minGenTime` -* `minSpeed = 1/maxGenTime` + HEAD.diffNumber = HEAD.number + dtDiffEval -###### Rules +Else if `HEAD~1.diffNumber <= HEAD.number`: -We define `PPowMin` as the `PowMin` value of the previous block. + HEAD.diffNumber = HEAD~1.diffNumber + dtDiffEval -* If the incoming block's `Number` is greater than 0 and a multiple of `dtDiffEval`, then: - * If `speed` is greater than or equal to `maxSpeed`, then: - * if `(PPoWMin + 2) % 16 == 0` then `PoWMin = PPoWMin + 2` - * else `PoWMin = PPoWMin + 1` - * If `speed` is less than or equal to `minSpeed`, then: - * if `(PPoWMin) % 16 == 0` then `PoWMin = MAX(0, PPoWMin - 2)` - * else `PoWMin = MAX(0, PPoWMin - 1)` -* Else - * If `Number` is greater than 0, `PoWMin` must be equal to the previous block's `PoWMin` +Else: -##### PreviousHash + HEAD.diffNumber = HEAD~1.diffNumber -* A block's `PreviousHash` must be exactly equal to the previous block's computed hash (a.k.a Proof-of-Work). Note that this hash **must** start with ` powZeroMin` zeros. +###### BR_G10 - HEAD.membersCount -##### PreviousIssuer +If `HEAD.number == 0`: -* A block's `PreviousIssuer` must be exactly equal to the previous block's `Issuer` field. + HEAD.membersCount = COUNT(LOCAL_IINDEX[member=true]) + +Else: -##### IssuersFrame + HEAD.membersCount = HEAD~1.membersCount + COUNT(LOCAL_IINDEX[member=true]) - COUNT(LOCAL_IINDEX[member=false]) -`IssuersFrame(t) = IssuersFrame(t-1) + CONVERGENCE` where: +###### BR_G11 - HEAD.udTime -* `IssuersFrame(t)` is the value of the field in the local block -* `IssuersFrame(t-1)` is the value of the field in the previous block (`1` in case of making root block or `40` if previous block is V2) -* `CONVERGENCE` equals `+1` if IssuersFrameVar(t-1) is `> 0`, or `-1` if IssuersFrameVar(t-1) is `< 0` +If `HEAD.number == 0`: -##### DifferentIssuersCount + HEAD.udTime = HEAD.medianTime + dt -This field counts the number of different block issuers between the `IssuersFrame(t-1)` previous blocks. +Else if `HEAD~1.udTime <= HEAD.medianTime`: -`IssuersFrame(t-1)`'s value is the one given by the previous block, or `0` when making a root block, or `40` if the previous block is V2. + HEAD.udTime = HEAD~1.udTime + dt -##### IssuersFrameVar +Else: -`IssuersFrameVar(t) = IssuersFrameVar(t-1) + NEW_ISSUER_INC - GONE_ISSUER_DEC + CONVERGENCE` where: + HEAD.udTime = HEAD~1.udTime -* `IssuersFrameVar(t)` is the value of the field in the local block -* `IssuersFrameVar(t-1)` is the value of the field in the previous block (`0` for root block or if previous block is V2) -* `NEW_ISSUER_INC` equals 5 if `DifferentIssuersCount` from local block equals `DifferentIssuersCount` of the previous block `+1` (equals `0` if previous block is V2) -* `GONE_ISSUER_DEC` equals 5 if `DifferentIssuersCount` from local block equals `DifferentIssuersCount` of the previous block `-1` (equals `0` if previous block is V2) -* `CONVERGENCE` equals `-1` if IssuersFrameVar(t-1) is `> 0`, or `+1` if IssuersFrameVar(t-1) is `< 0` +###### BR_G12 - HEAD.unitBase -##### Dates +If `HEAD.number == 0`: -* For any non-root block, `MedianTime` must be equal to the median value of the `Time` field for the last `medianTimeBlocks` blocks. If the number of available blocks is an even value, the median is computed over the 2 centered values by an arithmetical median on them, ceil rounded. + HEAD.unitBase = 0 + +Else: -##### Identity + HEAD.unitBase = HEAD~1.unitBase -* An identity must be writable to be included in the block. -* The blockchain cannot contain two or more identities sharing a same `USER_ID`. -* The blockchain cannot contain two or more identities sharing a same `PUBLIC_KEY`. -* Block#0's identities' `BLOCK_UID` must be the special value `0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855`. -* Other blocks' identities' `BLOCK_UID` field must match an existing block in the blockchain. +###### BR_G13 - HEAD.dividend and HEAD.new_dividend -##### Joiners, Actives, Leavers (block fingerprint based memberships) +If `HEAD.number == 0`: -* A membership must not be expired. -* Block#0's memberships' `NUMBER` must be `0` and `HASH` the special value `E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855` (SHA1 of empty string). -* Other blocks' memberships' `NUMBER` and `HASH` field must match an existing block in the blockchain. -* Each membership's `NUMBER` must be higher than the previous membership's `NUMBER` of the same issuer. + HEAD.dividend = ud0 + HEAD.new_dividend = null -##### Joiners, Actives (Web of Trust distance constraint) +If `HEAD.udTime != HEAD~1.udTime`: -* A given `PUBLIC_KEY` cannot be in `Joiners` if it does not exist, for at least `xpercent`% of the sentries, a path using certifications (this block included) leading to the key `PUBLIC_KEY` with a maximum count of `[stepMax]` hops. + HEAD.dividend = CEIL(HEAD~1.dividend + c² * HEAD~1.mass / POW(10, HEAD~1.unitbase) / HEAD.membersCount) + HEAD.new_dividend = HEAD.dividend -##### Joiners +Else: -* A revoked public key **cannot** be in `Joiners` -* A given `PUBLIC_KEY` cannot be in `Joiners` if it is a member. -* A given `PUBLIC_KEY` cannot be in `Joiners` if it does not have `[sigQty]` active certifications coming *to* it (incoming block included) -* `PUBLIC_KEY` must match for exactly one identity of the blockchain (incoming block included). + HEAD.dividend = HEAD~1.dividend + HEAD.new_dividend = null + +###### BR_G14 - HEAD.dividend and HEAD.unitbase and HEAD.new_dividend -##### Actives +If `HEAD.dividend >= 1000000` : -* A given `PUBLIC_KEY` **can** be in `Actives` **only if** it is a member. + HEAD.dividend = CEIL(HEAD.dividend / 10) + HEAD.new_dividend = HEAD.dividend + HEAD.unitBase = HEAD.unitBase + 1 + +###### BR_G15 - HEAD.mass -##### Leavers +If `HEAD.number == 0`: -* A given `PUBLIC_KEY` cannot be in `Leavers` if it is not a member. + HEAD.mass = 0 + +Else if `HEAD.udTime != HEAD~1.udTime`: -##### Revoked + HEAD.mass = HEAD~1.mass + HEAD.dividend * POWER(10, HEAD.unitBase) * HEAD.membersCount -* A given `PUBLIC_KEY` cannot be in `Revoked` if it has never been a member. -* A given `PUBLIC_KEY` cannot be in `Revoked` if its identity is already revoked. -* A given `PUBLIC_KEY` cannot be in `Revoked` if its revocation signature does not match. +Else: -##### Excluded + HEAD.mass = HEAD~1.mass -* A given `PUBLIC_KEY` cannot be in `Excluded` if it is not a member -* Each `PUBLIC_KEY` with less than `[sigQty]` active certifications or whose last membership is either in `Joiners` or `Actives` is outdated **must** be present in this field. -* Each `PUBLIC_KEY` whose last membership occurrence is either in `Joiners` or `Actives` *and* is outdated **must** be present in this field. -* A given `PUBLIC_KEY` **cannot** be in `Excluded` field if it doesn't **have to** (i.e. if no **must** condition is matched). +###### BR_G16 - HEAD.speed -##### Certifications +If `HEAD.number == 0`: -* A certification's `PUBKEY_FROM` must be a member. -* A certification must be writable. -* A certification must not be expired. -* A certification's `PUBKEY_TO`'s last membership occurrence **must not** be in `Leavers`. -* A certification's `PUBKEY_TO` must be a member **or** be in the incoming block's `Joiners`. -* A certification's signature must be valid over `PUBKEY_TO`'s self-certification, where signatory is `PUBKEY_FROM`. -* Replayability: there cannot exist 2 *active* certifications with the same `PUBKEY_FROM` and `PUBKEY_TO`. -* Chainability: a certification whose `PUBKEY_FROM` is the same than an existing certification in the blockchain can be written **only if** the last written certification (incoming block excluded) is considered chainable. + speed = 0 + +Else: -##### MembersCount + range = MIN(dtDiffEval, HEAD.number) + elapsed = (HEAD.medianTime - HEAD~<range>.medianTime) + +EndIf -`MembersCount` field must be equal to the last block's `MembersCount` plus the incoming block's `Joiners` count, minus this block's `Excluded` count. +If `elapsed == 0`: -##### Proof-of-Work + speed = 100 -> As of Version 5. +Else: + speed = range / elapsed -To be valid, a block fingerprint (whole document + signature) must start with a specific number of zeros + a remaining mark character. Rules are the following, and **each relative to a particular member**: +###### BR_G17 - HEAD.powMin -``` -PERSONAL_EXCESS = MAX(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1) -PERSONAL_HANDICAP = FLOOR(LN(1 + PERSONAL_EXCESS) / LN(1.189)) -PERSONAL_DIFF = MAX [ PoWMin ; PoWMin * FLOOR (percentRot * nbPreviousIssuers / (1 + nbBlocksSince)) ] + PERSONAL_HANDICAP +If `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed AND (HEAD~1.powMin + 2) % 16 == 0`: -if (PERSONAL_DIFF + 1) % 16 == 0 then PERSONAL_DIFF = PERSONAL_DIFF + 1 + HEAD.powMin = HEAD~1.powMin + 2 + +Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed >= maxSpeed`: -REMAINDER = PERSONAL_DIFFICULTY % 16 -NB_ZEROS = (PERSONAL_DIFFICULTY - REMAINDER) / 16 -``` + HEAD.powMin = HEAD~1.powMin + 1 + +Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed AND (HEAD~1.powMin) % 16 == 0`: -Where: + HEAD.powMin = MAX(0, HEAD~1.powMin - 2) + +Else if `HEAD.number > 0 AND HEAD.diffNumber != HEAD~1.diffNumber AND HEAD.speed <= minSpeed`: -* `[nbPersonalBlocksInFrame]` is the number of blocks written by the member from block (current `number` - current `issuersFrame` + 1) to current block (included) -* `[medianOfBlocksInFrame]` is the median quantity of blocks issued per member from block (current `number` - current `issuersFrame` + 1) to current block (included) -* `[PoWMin]` is the `PoWMin` value of the incoming block -* `[percentRot]` is the protocol parameter -* `[nbPreviousIssuers] = DifferentIssuersCount(last block of issuer)` -* `[nbBlocksSince]` is the number of blocks written **since** the last block of the member (so, incoming block excluded). - - -* If no block has been written by the member: - * `[nbPreviousIssuers] = 0` - * `[nbBlocksSince] = 0` - -The proof is considered valid if: - -* the proof starts with at least `NB_ZEROS` zeros -* the `NB_ZEROS + 1`th character is: - * between `[0-F]` if `REMAINDER = 0` - * between `[0-E]` if `REMAINDER = 1` - * between `[0-D]` if `REMAINDER = 2` - * between `[0-C]` if `REMAINDER = 3` - * between `[0-B]` if `REMAINDER = 4` - * between `[0-A]` if `REMAINDER = 5` - * between `[0-9]` if `REMAINDER = 6` - * between `[0-8]` if `REMAINDER = 7` - * between `[0-7]` if `REMAINDER = 8` - * between `[0-6]` if `REMAINDER = 9` - * between `[0-5]` if `REMAINDER = 10` - * between `[0-4]` if `REMAINDER = 11` - * between `[0-3]` if `REMAINDER = 12` - * between `[0-2]` if `REMAINDER = 13` - * between `[0-1]` if `REMAINDER = 14` - -> N.B.: it is not possible to have REMAINDER = 15 - -> Those rules of difficulty adaptation ensure a shared control of the blockchain writing. + HEAD.powMin = MAX(0, HEAD~1.powMin - 1) -##### Universal Dividend +Else if `HEAD.number > 0`: + + HEAD.powMin = HEAD~1.powMin -* Root blocks do not have the `UniversalDividend` field. -* Universal Dividend must be present if `MedianTime` value is greater than or equal to `lastUDTime` + `dt`. - * `lastUDTime` is the `MedianTime` of the last block with `UniversalDividend` in it. - * Initial value of `lastUDTime` equals to the root block's `MedianTime`. -* UD(t = 0) = `ud0` -* Value of `UniversalDividend` (`UD(t+1)`) equals to: +###### BR_G18 - HEAD.powZeros and HEAD.powRemainder -``` -UD(t+1) = INTEGER_PART(UD(t) + c² * M(t) / N(t+1)) -``` +If `HEAD.number == 0`: -Where: + nbPersonalBlocksInFrame = 0 + medianOfBlocksInFrame = 1 + +Else: -* `t` is UD time -* `UD(t)` is last UD value -* `c` equals to `[c]` parameter of this protocol -* `N(t+1)` equals to this block's `MembersCount` field -* `M(t)` equals to the sum of all `UD(t)*N(t)` of the blockchain (from t = 0, to t = now) where: - * `N(t)` is the `MembersCount` for `UD(t)` - * `UD(0)` equals to `[ud0]` parameter of this protocol + blocksOfIssuer = HEAD~1..<HEAD~1.issuersFrame>[issuer=HEAD.issuer] + nbPersonalBlocksInFrame = COUNT(blocksOfIssuer) + blocksPerIssuerInFrame = MAP( + UNIQ((HEAD~1..<HEAD~1.issuersFrame>).issuer) + => COUNT(HEAD~1..<HEAD~1.issuersFrame>[issuer=HEAD.issuer])) + medianOfBlocksInFrame = MEDIAN(blocksPerIssuerInFrame) + +EndIf + +If `nbPersonalBlocksInFrame == 0`: + + nbPreviousIssuers = 0 + nbBlocksSince = 0 -###### UD overflow -If the `UniversalDividend` value is higher than or equal to `1000000` (1 million), then the `UniversalDividend` value has to be: +Else: - UD(t+1) = CEIL(UD(t+1) / 10) + last = FIRST(blocksOfIssuer) + nbPreviousIssuers = last.issuersCount + nbBlocksSince = HEAD~1.number - last.number + +EndIf + + PERSONAL_EXCESS = MAX(0, ( (nbPersonalBlocksInFrame + 1) / medianOfBlocksInFrame) - 1) + PERSONAL_HANDICAP = FLOOR(LN(1 + PERSONAL_EXCESS) / LN(1.189)) + HEAD.issuerDiff = MAX [ HEAD.powMin ; HEAD.powMin * FLOOR (percentRot * nbPreviousIssuers / (1 + nbBlocksSince)) ] + PERSONAL_HANDICAP -and `UnitBase` value must be incremented by `1` compared to its value at `UD(t)` (see UnitBase global rule). +If `(HEAD.issuerDiff + 1) % 16 == 0`: -##### UnitBase + HEAD.issuerDiff = HEAD.issuerDiff + 1 -The field must be either equal to: +EndIf -* `0` for root block -* the previous block's `UnitBase` value if UD has no overflow -* the previous block's `UnitBase` value `+ 1` if UD has an overflow +Finally: -##### Transactions + HEAD.powRemainder = HEAD.issuerDiff % 16 + HEAD.powZeros = (HEAD.issuerDiff - HEAD.powRemainder) / 16 -* For `D` sources, public key must be a member for the block `#NUMBER` (so, *before* the block's memberships were applied) -* For `T` sources, the attached unlock condition must match -* Transaction cannot be included if `BLOCK_MEDIAN_TIME - MOST_RECENT_INPUT_TIME < LOCKTIME` +###### Local IINDEX augmentation -###### Amounts +####### BR_G19 - ENTRY.age + +For each ENTRY in local IINDEX where `op = 'CREATE'`: + + REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)] + +If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`: + + ENTRY.age = 0 + +Else if `REF_BLOC != null`: + + ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime + +Else: + + ENTRY.age = conf.idtyWindow + 1 + +EndIf + +###### BR_G20 - Identity UserID unicity + +For each ENTRY in local IINDEX: + +If `op = 'CREATE'`: + + ENTRY.uidUnique = COUNT(GLOBAL_IINDEX[uid=ENTRY.uid) == 0 + +Else: + + ENTRY.uidUnique = true + +###### BR_G21 - Identity pubkey unicity + +For each ENTRY in local IINDEX: + +If `op = 'CREATE'`: + + ENTRY.pubUnique = COUNT(GLOBAL_IINDEX[pub=ENTRY.pub) == 0 + +Else: + + ENTRY.pubUnique = true + +####### BR_G33 - ENTRY.excludedIsMember + +For each ENTRY in local IINDEX where `member != false`: + + ENTRY.excludedIsMember = true + +For each ENTRY in local IINDEX where `member == false`: + + ENTRY.excludedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member + +####### BR_G35 - ENTRY.isBeingKicked + +For each ENTRY in local IINDEX where `member != false`: + + ENTRY.isBeingKicked = false + +For each ENTRY in local IINDEX where `member == false`: + + ENTRY.isBeingKicked = true + +####### BR_G36 - ENTRY.hasToBeExcluded + +For each ENTRY in local IINDEX: + + ENTRY.hasToBeExcluded = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).kick + +###### Local MINDEX augmentation + +####### BR_G22 - ENTRY.age + +For each ENTRY in local MINDEX where `revoked_on == null`: + + REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)] + +If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`: + + ENTRY.age = 0 + +Else if `REF_BLOC != null`: + + ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime + +Else: + + ENTRY.age = conf.msWindow + 1 + +EndIf + +####### BR_G23 - ENTRY.numberFollowing + +For each ENTRY in local MINDEX where `revoked_on == null`: + + created_on = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).created_on + +If `created_on != null`: + + ENTRY.numberFollowing = NUMBER(ENTRY.created_ON) > NUMBER(created_on) + +Else: + + ENTRY.numberFollowing = true + +EndIf + +For each ENTRY in local MINDEX where `revoked_on != null`: + + ENTRY.numberFollowing = true + +####### BR_G24 - ENTRY.distanceOK + +For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`: + + dSen = CEIL(HEAD.membersCount ^ (1 / stepMax)) + + GRAPH = SET(LOCAL_CINDEX, 'issuer', 'receiver') + SET(GLOBAL_CINDEX, 'issuer', 'receiver') + SENTRIES = SUBSET(GRAPH, dSen, 'issuer') + + ENTRY.distanceOK = EXISTS_PATH(xpercent, SENTRIES, GRAPH, ENTRY.pub, stepMax) + +> Functionally: checks if it exists, for at least `xpercent`% of the sentries, a path using GLOBAL_CINDEX + LOCAL_CINDEX leading to the key `PUBLIC_KEY` with a maximum count of `[stepMax]` hops. + +For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`: + + ENTRY.distanceOK = true + +####### BR_G25 - ENTRY.onRevoked + +For each ENTRY in local MINDEX: + + ENTRY.onRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null + +####### BR_G26 - ENTRY.joinsTwice + +For each ENTRY in local MINDEX where `op = 'UPDATE', expired_on = 0`: + + ENTRY.joinsTwice = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member == true + +####### BR_G27 - ENTRY.enoughCerts + +For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`: + + ENTRY.enoughCerts = COUNT(GLOBAL_CINDEX[receiver=ENTRY.pub,expired_on=null]) + COUNT(LOCAL_CINDEX[receiver=ENTRY.pub,expired_on=null]) >= sigQty + +> Functionally: any member or newcomer needs `[sigQty]` certifications coming *to* him to be in the WoT + +For each ENTRY in local MINDEX where `!(type == 'JOIN' OR type == 'ACTIVE')`: + + ENTRY.enoughCerts = true + +####### BR_G28 - ENTRY.leaverIsMember + +For each ENTRY in local MINDEX where `type == 'LEAVE'`: + + ENTRY.leaverIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member + +For each ENTRY in local MINDEX where `type != 'LEAVE'`: + + ENTRY.leaverIsMember = true + +####### BR_G29 - ENTRY.activeIsMember + +For each ENTRY in local MINDEX where `type == 'ACTIVE'`: + + ENTRY.activeIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member + +For each ENTRY in local MINDEX where `type != 'ACTIVE'`: + + ENTRY.activeIsMember = true + +####### BR_G30 - ENTRY.revokedIsMember + +For each ENTRY in local MINDEX where `revoked_on == null`: + + ENTRY.revokedIsMember = true + +For each ENTRY in local MINDEX where `revoked_on != null`: + + ENTRY.revokedIsMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member + +####### BR_G31 - ENTRY.alreadyRevoked + +For each ENTRY in local MINDEX where `revoked_on == null`: + + ENTRY.alreadyRevoked = false + +For each ENTRY in local MINDEX where `revoked_on != null`: + + ENTRY.alreadyRevoked = REDUCE(GLOBAL_MINDEX[pub=ENTRY.pub]).revoked_on != null + +####### BR_G32 - ENTRY.revocationSigOK + +For each ENTRY in local MINDEX where `revoked_on == null`: + + ENTRY.revocationSigOK = true + +For each ENTRY in local MINDEX where `revoked_on != null`: + + ENTRY.revocationSigOK = SIG_CHECK_REVOKE(REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]), ENTRY) + +####### BR_G34 - ENTRY.isBeingRevoked + +For each ENTRY in local MINDEX where `revoked_on == null`: + + ENTRY.isBeingRevoked = false + +For each ENTRY in local MINDEX where `revoked_on != null`: + + ENTRY.isBeingRevoked = true + +###### Local CINDEX augmentation + +For each ENTRY in local CINDEX: + +####### BR_G37 - ENTRY.age + + REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.created_on)>[hash=HASH(ENTRY.created_on)] + +If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`: + + ENTRY.age = 0 + +Else if `REF_BLOC != null`: + + ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime + +Else: + + ENTRY.age = conf.sigWindow + 1 + +EndIf + +####### BR_G38 - ENTRY.unchainables + +If `HEAD.number > 0`: + + ENTRY.unchainables = COUNT(GLOBAL_CINDEX[issuer=ENTRY.issuer, chainable_on > HEAD~1.medianTime])) + +####### BR_G39 - ENTRY.stock + + ENTRY.stock = COUNT(GLOBAL_CINDEX[issuer=ENTRY.issuer, expired_on=0])) + +####### BR_G40 - ENTRY.fromMember + + ENTRY.fromMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.issuer]).member + +####### BR_G41 - ENTRY.toMember + + ENTRY.toMember = REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]).member + +####### BR_G42 - ENTRY.toNewcomer + + ENTRY.toNewcomer = COUNT(LOCAL_IINDEX[member=true,pub=ENTRY.receiver]) > 0 + +####### BR_G43 - ENTRY.toLeaver + + ENTRY.toLeaver = REDUCE(GLOBAL_MINDEX[pub=ENTRY.receiver]).leaving + +####### BR_G44 - ENTRY.isReplay + + reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0] + +If `count(reducable) == 0`: + + ENTRY.isReplay = false + +Else: + + ENTRY.isReplay = reduce(reducable).expired_on == 0 + +####### BR_G45 - ENTRY.sigOK + + ENTRY.sigOK = SIG_CHECK_CERT(REDUCE(GLOBAL_IINDEX[pub=ENTRY.receiver]), ENTRY) + +###### Local SINDEX augmentation + +####### BR_G102 - ENTRY.age + +For each ENTRY in local IINDEX where `op = 'UPDATE'`: + + REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.hash)>[hash=HASH(ENTRY.created_on)] + +If `HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'`: + + ENTRY.age = 0 + +Else if `REF_BLOC != null`: + + ENTRY.age = HEAD~1.medianTime - REF_BLOCK.medianTime + +Else: + + ENTRY.age = conf.txWindow + 1 + +EndIf + +####### BR_G46 - ENTRY.available + +For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`: + + ENTRY.available = REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).consumed == false + +####### BR_G47 - ENTRY.isLocked + + ENTRY.isLocked = TX_SOURCE_UNLOCK(REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).conditions, ENTRY) + +####### BR_G48 - ENTRY.isTimeLocked + + ENTRY.isTimeLocked = ENTRY.written_time - REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).written_time < ENTRY.locktime + +##### Rules + +###### BR_G49 - Version + +Rule: + +If `HEAD.number > 0`: + + HEAD.version == (HEAD~1.version OR HEAD~1.version + 1) + +###### BR_G50 - Block size + +Rule: + + HEAD.size < MAX(500 ; CEIL(1.10 * HEAD.avgBlockSize)) + +###### BR_G98 - Currency + +Rule: + +If `HEAD.number > 0`: + + Currency = HEAD.currency + +###### BR_G51 - Number + +Rule: + + Number = HEAD.number + +###### BR_G52 - PreviousHash + +Rule: + + PreviousHash = HEAD.previousHash + +###### BR_G53 - PreviousIssuer + +Rule: + + PreviousHash = HEAD.previousHash + +###### BR_G101 - Issuer + +Rule: + + HEAD.issuerIsMember == true + +###### BR_G54 - DifferentIssuersCount + +Rule: + +If `HEAD.version > 2`: + + DifferentIssuersCount = HEAD.issuersCount + +###### BR_G55 - IssuersFrame + +Rule: + +If `HEAD.version > 2`: + + IssuersFrame = HEAD.issuersFrame + +###### BR_G56 - IssuersFrameVar + +Rule: + +If `HEAD.version > 2`: + + IssuersFrameVar = HEAD.issuersFrameVar + +###### BR_G57 - MedianTime + +Rule: + + MedianTime = HEAD.medianTime + +###### BR_G58 - UniversalDividend + +Rule: + + UniversalDividend = HEAD.new_dividend + +###### BR_G59 - UnitBase + +Rule: + + UnitBase = HEAD.unitBase + +###### BR_G60 - MembersCount + +Rule: + + MembersCount = HEAD.membersCount + +###### BR_G61 - PowMin + +Rule: + +If `HEAD.number > 0`: + + PowMin = HEAD.powMin + +###### BR_G62 - Proof-of-work + +Rule: the proof is considered valid if: + +* `HEAD.hash` starts with at least `HEAD.powZeros` zeros +* `HEAD.hash`'s `HEAD.powZeros + 1`th character is: + * between `[0-F]` if `HEAD.powRemainder = 0` + * between `[0-E]` if `HEAD.powRemainder = 1` + * between `[0-D]` if `HEAD.powRemainder = 2` + * between `[0-C]` if `HEAD.powRemainder = 3` + * between `[0-B]` if `HEAD.powRemainder = 4` + * between `[0-A]` if `HEAD.powRemainder = 5` + * between `[0-9]` if `HEAD.powRemainder = 6` + * between `[0-8]` if `HEAD.powRemainder = 7` + * between `[0-7]` if `HEAD.powRemainder = 8` + * between `[0-6]` if `HEAD.powRemainder = 9` + * between `[0-5]` if `HEAD.powRemainder = 10` + * between `[0-4]` if `HEAD.powRemainder = 11` + * between `[0-3]` if `HEAD.powRemainder = 12` + * between `[0-2]` if `HEAD.powRemainder = 13` + * between `[0-1]` if `HEAD.powRemainder = 14` + +> N.B.: it is not possible to have HEAD.powRemainder = 15 + +###### BR_G63 - Identity writability + +Rule: + + ENTRY.age <= [idtyWindow] + +###### BR_G64 - Membership writability + +Rule: + + ENTRY.age <= [msWindow] + +###### BR_G65 - Certification writability + +Rule: + + ENTRY.age <= [sigWindow] + +###### BR_G66 - Certification stock + +Rule: + + ENTRY.stock <= sigStock + +###### BR_G67 - Certification period + +Rule: + + ENTRY.unchainables == 0 + +###### BR_G68 - Certification from member + +Rule: + +If `HEAD.number > 0`: + + ENTRY.fromMember == true + +###### BR_G69 - Certification to member or newcomer + +Rule: + + ENTRY.toMember == true + OR + ENTRY.toNewcomer == true + +###### BR_G70 - Certification to non-leaver + +Rule: + + ENTRY.toLeaver == false + +###### BR_G71 - Certification replay + +Rule: + + ENTRY.isReplay == false + +###### BR_G72 - Certification signature + +Rule: + + ENTRY.sigOK == true + +###### BR_G73 - Identity UserID unicity + +Rule: + + ENTRY.uidUnique == true + +###### BR_G74 - Identity pubkey unicity + +Rule: + + ENTRY.pubUnique == true + +###### BR_G75 - Membership succession + +Rule: + + ENTRY.numberFollowing == true + +###### BR_G76 - Membership distance check + +Rule: + + ENTRY.distanceOK == true + +###### BR_G77 - Membership on revoked + +Rule: + + ENTRY.onRevoked == false + +###### BR_G78 - Membership joins twice + +Rule: + + ENTRY.joinsTwice == false + +###### BR_G79 - Membership enough certifications + +Rule: + + ENTRY.enoughCerts == true + +###### BR_G80 - Membership leaver + +Rule: + + ENTRY.leaverIsMember == true + +###### BR_G81 - Membership active + +Rule: + + ENTRY.activeIsMember == true + +###### BR_G82 - Revocation by a member + +Rule: + + ENTRY.revokedIsMember == true + +###### BR_G83 - Revocation singleton + +Rule: + + ENTRY.alreadyRevoked == false + +###### BR_G84 - Revocation signature + +Rule: + + ENTRY.revocationSigOK == true + +###### BR_G85 - Excluded is a member + +Rule: + + ENTRY.excludedIsMember == true + +###### BR_G86 - Excluded to be kicked + +Rule: + +For each `REDUCE_BY(GLOBAL_IINDEX[kick=true], 'pub') as TO_KICK`: + + REDUCED = REDUCE(GLOBAL_IINDEX[pub=TO_KICK.pub]) + +If `REDUCED.kick` then: + + COUNT(LOCAL_MINDEX[pub=REDUCED.pub,isBeingKicked=true]) == 1 + +###### BR_G103 - Trancation writability + +Rule: + + ENTRY.age <= [txWindow] + +###### BR_G87 - Input is available + +For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`: + +Rule: + + ENTRY.available == true + +###### BR_G88 - Input is unlocked + +For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`: + +Rule: + + ENTRY.isLocked == false + +###### BR_G89 - Input is time unlocked + +For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`: + +Rule: + + ENTRY.isTimeLocked == false + +###### BR_G90 - Output base + +For each `LOCAL_SINDEX[op='CREATE'] as ENTRY`: + +Rule: + + ENTRY.unitBase <= HEAD~1.unitBase + +##### Post-rules INDEX augmentation + +###### BR_G91 - Dividend + +If `HEAD.new_dividend != null`: + +For each `REDUCE_BY(GLOBAL_IINDEX[member=true], 'pub') as IDTY` then if `IDTY.member`, add a new LOCAL_SINDEX entry: + + SINDEX ( + op = 'CREATE' + identifier = IDTY.pub + pos = HEAD.number + written_on = BLOCKSTAMP + written_time = MedianTime + amount = HEAD.dividend + base = HEAD.unitBase + locktime = null + conditions = REQUIRE_SIG(MEMBER.pub) + consumed = false + ) + +###### BR_G92 - Certification expiry + +For each `GLOBAL_CINDEX[expires_on<=HEAD.medianTime] as CERT`, add a new LOCAL_CINDEX entry: + +If `reduce(GLOBAL_CINDEX[issuer=CERT.issuer,receiver=CERT.receiver,created_on=CERT.created_on]).expired_on == 0`: + + CINDEX ( + op = 'UPDATE' + issuer = CERT.issuer + receiver = CERT.receiver + created_on = CERT.created_on + expired_on = HEAD.medianTime + ) + +###### BR_G93 - Membership expiry + +For each `REDUCE_BY(GLOBAL_MINDEX[expires_on<=HEAD.medianTime], 'pub') as POTENTIAL` then consider `REDUCE(GLOBAL_MINDEX[pub=POTENTIAL.pub]) AS MS`. + +If `MS.expired_on == null OR MS.expired_on == 0`, add a new LOCAL_MINDEX entry: + + MINDEX ( + op = 'UPDATE' + pub = MS.pub + written_on = BLOCKSTAMP + expired_on = HEAD.medianTime + ) + +###### BR_G94 - Exclusion by membership + +For each `LOCAL_MINDEX[expired_on!=0] as MS`, add a new LOCAL_IINDEX entry: + + IINDEX ( + op = 'UPDATE' + pub = MS.pub + written_on = BLOCKSTAMP + kick = true + ) + +###### BR_G95 - Exclusion by certification + +For each `LOCAL_CINDEX[expired_on!=0] as CERT`: + +If `COUNT(GLOBAL_CINDEX[receiver=CERT.receiver]) + COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on=0]) - COUNT(LOCAL_CINDEX[receiver=CERT.receiver,expired_on!=0]) < sigQty`, add a new LOCAL_IINDEX entry: + + IINDEX ( + op = 'UPDATE' + pub = CERT.receiver + written_on = BLOCKSTAMP + kick = true + ) + +###### BR_G96 - Implicit revocation + +For each `GLOBAL_MINDEX[revokes_on<=HEAD.medianTime,revoked_on=null] as MS`: + + REDUCED = REDUCE(GLOBAL_MINDEX[pub=MS.pub]) + +If `REDUCED.revokes_on<=HEAD.medianTime AND REDUCED.revoked_on==null`, add a new LOCAL_MINDEX entry: + + MINDEX ( + op = 'UPDATE' + pub = MS.pub + written_on = BLOCKSTAMP + revoked_on = HEAD.medianTime + ) + +###### BR_G104 - Membership expiry date correction + +For each `LOCAL_MINDEX[type='JOIN'] as MS`: + + MS.expires_on = MS.expires_on - MS.age + MS.revokes_on = MS.revokes_on - MS.age + +For each `LOCAL_MINDEX[type='ACTIVE'] as MS`: + + MS.expires_on = MS.expires_on - MS.age + MS.revokes_on = MS.revokes_on - MS.age + +###### BR_G105 - Certification expiry date correction + +For each `LOCAL_CINDEX as CERT`: + + CERT.expires_on = CERT.expires_on - CERT.age + +##### BR_G97 - Final INDEX operations -* *Rule*: For each UD source, the amount must match the exact targeted UD value -* *Def.*: `MaxOutputBase` is the maximum value of all `OutputBase` -* *Rule*: `MaxOutputBase` cannot be higher than the current block's `UnitBase` +If all the rules [BR_G49 ; BR_G90] pass, then all the LOCAL INDEX values (IINDEX, MINDEX, CINDEX, SINDEX, BINDEX) have to be appended to the GLOBAL INDEX. ### Peer diff --git a/server.js b/server.js index 5f57029ce5075bc10f5ceba6257e2ffefd6b4256..b52289efe56d0d02ffe07ce975cf3f8b4fe4a157 100644 --- a/server.js +++ b/server.js @@ -72,6 +72,8 @@ function Server (dbConf, overrideConf) { } }; + this.getBcContext = () => this.BlockchainService.getContext(); + this.plugFileSystem = () => co(function *() { logger.debug('Plugging file system...'); const params = yield paramsP; diff --git a/test/data/blocks.js b/test/data/blocks.js index cb3c3b8a19c3e688b09efbec3892d6588a33e0ee..7ca50f030c6626529a6c4637e206397f1a389476 100644 --- a/test/data/blocks.js +++ b/test/data/blocks.js @@ -1325,155 +1325,7 @@ module.exports = { "Nonce: 5\n" + "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - ROOT_BLOCK_REQUIRED: - "Version: 2\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 1\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - V2_CANNOT_FOLLOW_V3: - "Version: 2\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 1\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - V3_CANNOT_FOLLOW_V4: - "Version: 3\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 51\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "UnitBase: 2\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "IssuersFrame: 100\n" + - "IssuersFrameVar: 0\n" + - "DifferentIssuersCount: 3\n" + - "PreviousHash: 4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - SAME_BLOCK_NUMBER: - "Version: 2\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 50\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - OLD_BLOCK_NUMBER: - "Version: 2\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 49\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - FAR_FUTURE_BLOCK_NUMBER: - "Version: 2\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 52\n" + - "PoWMin: 1\n" + - "Time: 1411776000\n" + - "MedianTime: 1411776000\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "PreviousHash: 52DC8A585C5D89571C511BB83F7E7D3382F0041452064B1272E65F0B42B82D57\n" + - "PreviousIssuer: G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - - WRONG_PREVIOUS_HASH: + WRONG_PREVIOUS_HASH: "Version: 2\n" + "Type: Block\n" + "Currency: beta_brousouf\n" + @@ -3006,36 +2858,6 @@ module.exports = { "Nonce: 1\n" + "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - V3_HAS_MAXIMUM_SIZE: - "Version: 3\n" + - "Type: Block\n" + - "Currency: beta_brousouf\n" + - "Number: 83\n" + - "PoWMin: 1\n" + - "Time: 1411777000\n" + - "MedianTime: 1411777000\n" + - "UniversalDividend: 100\n" + - "UnitBase: 0\n" + - "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "IssuersFrame: 101\n" + - "IssuersFrameVar: 11\n" + - "DifferentIssuersCount: 4\n" + - "PreviousHash: 2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" + - "PreviousIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + - "MembersCount: 3\n" + - "Identities:\n" + - "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:YvMQqaOAgLtnJzg5ZGhI17sZvXjGgzpSMxNz8ikttMspU5/45MQAqnOfuJnfbrzkkspGlUUjDnUPsOmHPcVyBQ==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:CAT\n".repeat(544) + - "Joiners:\n" + - "Actives:\n" + - "Leavers:\n" + - "Revoked:\n" + - "Excluded:\n" + - "Certifications:\n" + - "Transactions:\n" + - "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + - "Nonce: 1\n" + - "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n", - TRANSACTION_WITHOUT_ISSUERS: "Version: 2\n" + "Type: Block\n" + diff --git a/test/fast/block/block_global.js b/test/fast/block/block_global.excluded similarity index 88% rename from test/fast/block/block_global.js rename to test/fast/block/block_global.excluded index b214fea2d25a2ed89b09922f5334eb856ba56e73..35a3cb581c60362034beefdf2a94d668e244534a 100644 --- a/test/fast/block/block_global.js +++ b/test/fast/block/block_global.excluded @@ -49,9 +49,19 @@ function getDAL(overrides) { }, overrides); } -describe("Block global coherence:", function(){ +/** + * TODO: reimplement tests according to new convention: + * + * - Name: protocol-brg<number>-<title>.js + * - Content: see existing tests + */ - it('a valid block should not have any error', validate(blocks.VALID_ROOT, getDAL(), function (err) { +describe.skip("Block global coherence:", function(){ + + it('a valid block should not have any error', validate(blocks.VALID_ROOT, getDAL(), { + getIssuerPersonalizedDifficulty: () => Q(1), + getvHEAD_1: () => Q({ version : 2 }) + }, function (err) { should.not.exist(err); })); @@ -68,73 +78,11 @@ describe("Block global coherence:", function(){ }, isMember: () => Q(true), getBlocksBetween: () => Q([{time:1411776000},{time:1411776000},{time:1411776000}]) - }), function (err) { - should.not.exist(err); - })); - - it('a V2 number cannot follow V3', test(rules.GLOBAL.checkVersion, blocks.V2_CANNOT_FOLLOW_V3, { - getCurrentBlockOrNull: () => Q({ - version: 3 - }) - }, function (err) { - should.exist(err); - err.message.should.equal('`Version: 2` must follow another V2 block or be the root block'); - })); - - it('a V2 number cannot follow V4', test(rules.GLOBAL.checkVersion, blocks.V2_CANNOT_FOLLOW_V3, { - getCurrentBlockOrNull: () => Q({ - version: 4 - }) - }, function (err) { - should.exist(err); - err.message.should.equal('`Version: 2` must follow another V2 block or be the root block'); - })); - - it('a V3 number cannot follow V4', test(rules.GLOBAL.checkVersion, blocks.V3_CANNOT_FOLLOW_V4, { - getCurrentBlockOrNull: () => Q({ - version: 4 - }) - }, function (err) { - should.exist(err); - err.message.should.equal('`Version: 3` must follow another V3 block, a V2 block or be the root block'); - })); - - it('a V3 block has a limited size', test(rules.GLOBAL.checkBlockLength, blocks.V3_HAS_MAXIMUM_SIZE, { - getCurrentBlockOrNull: () => Q({ version: 3, len: 200 }), - getBlocksBetween: () => Q([ - { len: 450 },{ len: 500 },{ len: 530 } - ]) - }, function (err) { - should.exist(err); - err.message.should.equal('Block size is too high'); - })); - - it('a block with positive number while no root exists should fail', test(rules.GLOBAL.checkNumber, blocks.ROOT_BLOCK_REQUIRED, { - getCurrentBlockOrNull: () => Q(null) - }, function (err) { - should.exist(err); - err.message.should.equal('Root block required first'); - })); - - it('a block with same number as current should fail', test(rules.GLOBAL.checkNumber, blocks.SAME_BLOCK_NUMBER, { - getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 }) + }), { + getIssuerPersonalizedDifficulty: () => Q(2), + getvHEAD_1: () => Q({ version : 2 }) }, function (err) { - should.exist(err); - err.message.should.equal('Too late for this block'); - })); - - it('a block with older number than current should fail', test(rules.GLOBAL.checkNumber, blocks.OLD_BLOCK_NUMBER, { - getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 }) - }, function (err) { - should.exist(err); - err.message.should.equal('Too late for this block'); - })); - - it('a block with too far future number than current should fail', test(rules.GLOBAL.checkNumber, blocks.FAR_FUTURE_BLOCK_NUMBER, { - getCurrentBlockOrNull: () => Q({ number: 50, hash: '4C8800825C44A22F230AFC0D140BF1930331A686899D16EBE4C58C9F34C609E8', issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', membersCount: 3 }) - }, function (err) { - should.exist(err); - err.message.should.equal('Too early for this block'); + should.not.exist(err); })); it('a block with wrong PreviousHash should fail', test(rules.GLOBAL.checkPreviousHash, blocks.WRONG_PREVIOUS_HASH, { @@ -393,46 +341,44 @@ describe("Block global coherence:", function(){ })); it('a block not starting with a leading zero should fail', test(rules.GLOBAL.checkProofOfWork, blocks.NO_LEADING_ZERO, { - getCurrentBlockOrNull: () => Q({ number: 2 }), - lastBlockOfIssuer: () => Q({ number: 2 }), - getBlock: () => Q({ powMin: 8 }), - getBlocksBetween: () => Q([{ issuer: 'a' }]) + bcContext: { + getIssuerPersonalizedDifficulty: () => Q(8) + } }, function (err) { should.exist(err); err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'F\', required was 0 zeros and an hexa char between [0-7]'); })); it('a block requiring 2 leading zeros but providing less should fail', test(rules.GLOBAL.checkProofOfWork, blocks.REQUIRES_7_LEADING_ZEROS, { - getCurrentBlockOrNull: () => Q({ number: 2 }), - lastBlockOfIssuer: () => Q({ number: 2 }), - getBlock: () => Q({ powMin: 6 }), - getBlocksBetween: () => Q([{ issuer: 'a' },{ issuer: 'b' }]) + bcContext: { + getIssuerPersonalizedDifficulty: () => Q(12) + } }, function (err) { should.exist(err); err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'B\', required was 0 zeros and an hexa char between [0-3]'); })); it('a block requiring 1 leading zeros but providing less should fail', test(rules.GLOBAL.checkProofOfWork, blocks.REQUIRES_6_LEADING_ZEROS, { - getCurrentBlockOrNull: () => Q({ number: 2 }), - lastBlockOfIssuer: () => Q({ number: 2 }), - getBlock: () => Q({ powMin: 8 }), - getBlocksBetween: () => Q([{ issuer: 'a' }]) + bcContext: { + getIssuerPersonalizedDifficulty: () => Q(8) + } }, function (err) { should.exist(err); err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'8\', 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(rules.GLOBAL.checkProofOfWork, blocks.FIRST_BLOCK_OF_NEWCOMER, { - getCurrentBlockOrNull: () => Q(null) + bcContext: { + getIssuerPersonalizedDifficulty: () => Q(1) + } }, function (err) { should.not.exist(err); })); it('a block requiring 40 leading zeros as second block of newcomer should fail', test(rules.GLOBAL.checkProofOfWork, blocks.SECOND_BLOCK_OF_NEWCOMER, { - getCurrentBlockOrNull: () => Q({ number: 2 }), - lastBlockOfIssuer: () => Q({ number: 2 }), - getBlock: () => Q({ powMin: 160 }), - getBlocksBetween: () => Q([{ issuer: 'a' }]) + bcContext: { + getIssuerPersonalizedDifficulty: () => Q(160) + } }, function (err) { should.exist(err); err.message.should.equal('Wrong proof-of-work level: given 0 zeros and \'F\', required was 10 zeros and an hexa char between [0-9A-F]'); @@ -701,7 +647,9 @@ function test (rule, raw, dal, callback) { return co(function *() { let obj = parser.syncWrite(raw); let block = new Block(obj); - if (rule.length == 2) { + if (rule == rules.GLOBAL.checkProofOfWork || rule == rules.GLOBAL.checkVersion) { + yield rule(block, dal.bcContext); + } else if (rule.length == 2) { yield rule(block, dal); } else { yield rule(block, conf, dal); @@ -711,14 +659,14 @@ function test (rule, raw, dal, callback) { }; } -function validate (raw, dal, callback) { +function validate (raw, dal, bcContext, callback) { var block; return function() { return Q.Promise(function(resolve, reject){ async.waterfall([ function (next){ block = new Block(parser.syncWrite(raw)); - rules.CHECK.ASYNC.ALL_GLOBAL(block, conf, dal, next); + rules.CHECK.ASYNC.ALL_GLOBAL(block, conf, dal, bcContext, next); } ], function (err) { err && console.error(err.stack); diff --git a/test/fast/block/block_local.js b/test/fast/block/block_local.js index 882f66eda57a9cfd064654dd768c470f40d1f164..ca8c79bded93404d7d4464cdbef9ca251dbf5e7e 100644 --- a/test/fast/block/block_local.js +++ b/test/fast/block/block_local.js @@ -54,7 +54,7 @@ describe("Block local coherence", function(){ it('Block cannot contain a same pubkey more than once in leavers', test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_LEAVES, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded')); it('Block cannot contain a same pubkey more than once in excluded', test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_EXCLUDED, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded')); it('Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded', test(rules.LOCAL.checkPubkeyUnicity, blocks.MULTIPLE_OVER_ALL, 'Block cannot contain a same pubkey more than once in joiners, actives, leavers and excluded')); - it('Block cannot have revoked key in joiners,actives,leavers', test(rules.LOCAL.checkRevokedNotInMemberships, blocks.REVOKED_WITH_MEMBERSHIPS, 'A revoked pubkey cannot have a membership in the same block')); + it('Block cannot have revoked key in joiners,actives,leavers', test(rules.LOCAL.checkMembershipUnicity, blocks.REVOKED_WITH_MEMBERSHIPS, 'Unicity constraint PUBLIC_KEY on MINDEX is not respected')); it('Block cannot have revoked key duplicates', test(rules.LOCAL.checkRevokedUnicity, blocks.REVOKED_WITH_DUPLICATES, 'A single revocation per member is allowed')); it('Block revoked keys must be in excluded', test(rules.LOCAL.checkRevokedAreExcluded, blocks.REVOKED_NOT_IN_EXCLUDED, 'A revoked member must be excluded')); it('Block cannot contain 2 certifications from same issuer', test(rules.LOCAL.checkCertificationOneByIssuer, blocks.MULTIPLE_CERTIFICATIONS_FROM_SAME_ISSUER, 'Block cannot contain two certifications from same issuer')); diff --git a/test/fast/block/protocol-brg49-version.js b/test/fast/block/protocol-brg49-version.js new file mode 100644 index 0000000000000000000000000000000000000000..1aa80be8a9a830faa2ebc4e5e95977a2659e821a --- /dev/null +++ b/test/fast/block/protocol-brg49-version.js @@ -0,0 +1,34 @@ +"use strict"; +const co = require('co'); +const should = require('should'); +const indexer = require('../../../app/lib/dup/indexer'); + +const FAIL = false; +const SUCCESS = true; + +describe("Protocol BR_G49 - Version", function(){ + + it('V3 following V2 should fail', () => co(function*(){ + const HEAD_1 = { number: 17, version: 3 }; + const HEAD = { number: 18, version: 2 }; + indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL); + })); + + it('V4 following V2 should fail', () => co(function*(){ + const HEAD_1 = { number: 17, version: 4 }; + const HEAD = { number: 18, version: 2 }; + indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL); + })); + + it('V3 following V4 should succeed', () => co(function*(){ + const HEAD_1 = { number: 17, version: 3 }; + const HEAD = { number: 18, version: 4 }; + indexer.ruleVersion(HEAD, HEAD_1).should.equal(SUCCESS); + })); + + it('V3 following V5 should fail', () => co(function*(){ + const HEAD_1 = { number: 17, version: 3 }; + const HEAD = { number: 18, version: 5 }; + indexer.ruleVersion(HEAD, HEAD_1).should.equal(FAIL); + })); +}); diff --git a/test/fast/block/protocol-brg50-blocksize.js b/test/fast/block/protocol-brg50-blocksize.js new file mode 100644 index 0000000000000000000000000000000000000000..475934eaf069d12140c16581dae8521dc82a16b0 --- /dev/null +++ b/test/fast/block/protocol-brg50-blocksize.js @@ -0,0 +1,50 @@ +"use strict"; +const co = require('co'); +const should = require('should'); +const indexer = require('../../../app/lib/dup/indexer'); + +const FAIL = false; +const SUCCESS = true; + +describe("Protocol BR_G50 - Block size", function(){ + + it('2 for an AVG(10) should succeed', () => co(function*(){ + const HEAD = { bsize: 2, avgBlockSize: 10 }; + indexer.ruleBlockSize(HEAD).should.equal(SUCCESS); + })); + + it('400 for an AVG(10) should succeed', () => co(function*(){ + const HEAD = { bsize: 400, avgBlockSize: 10 }; + indexer.ruleBlockSize(HEAD).should.equal(SUCCESS); + })); + + it('499 for an AVG(10) should succeed', () => co(function*(){ + const HEAD = { bsize: 499, avgBlockSize: 10 }; + indexer.ruleBlockSize(HEAD).should.equal(SUCCESS); + })); + + it('500 for an AVG(10) should fail', () => co(function*(){ + const HEAD = { bsize: 500, avgBlockSize: 10 }; + indexer.ruleBlockSize(HEAD).should.equal(FAIL); + })); + + it('500 for an AVG(454) should fail', () => co(function*(){ + const HEAD = { bsize: 500, avgBlockSize: 454 }; + indexer.ruleBlockSize(HEAD).should.equal(FAIL); + })); + + it('500 for an AVG(455) should succeed', () => co(function*(){ + const HEAD = { bsize: 500, avgBlockSize: 455 }; + indexer.ruleBlockSize(HEAD).should.equal(SUCCESS); + })); + + it('1100 for an AVG(1000) should fail', () => co(function*(){ + const HEAD = { bsize: 1100, avgBlockSize: 1000 }; + indexer.ruleBlockSize(HEAD).should.equal(FAIL); + })); + + it('1100 for an AVG(1001) should succeed', () => co(function*(){ + const HEAD = { bsize: 1100, avgBlockSize: 1001 }; + indexer.ruleBlockSize(HEAD).should.equal(SUCCESS); + })); +}); diff --git a/test/fast/block/protocol-brg51-number.js b/test/fast/block/protocol-brg51-number.js new file mode 100644 index 0000000000000000000000000000000000000000..12abe494540e15401dc9eded2d523bec30395b58 --- /dev/null +++ b/test/fast/block/protocol-brg51-number.js @@ -0,0 +1,67 @@ +"use strict"; +const co = require('co'); +const should = require('should'); +const indexer = require('../../../app/lib/dup/indexer'); + +const FAIL = false; +const SUCCESS = true; + +describe("Protocol BR_G51 - Number", function(){ + + it('1 following 1 should fail', () => co(function*(){ + const block = { number: 1 }; + const HEAD_1 = { number: 1 }; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(FAIL); + })); + + it('1 following 0 should succeed', () => co(function*(){ + const block = { number: 1 }; + const HEAD_1 = { number: 0 }; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(SUCCESS); + })); + + it('0 following 0 should fail', () => co(function*(){ + const block = { number: 0 }; + const HEAD_1 = { number: 0 }; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(FAIL); + })); + + it('0 following nothing should succeed', () => co(function*(){ + const block = { number: 0 }; + const HEAD_1 = null; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(SUCCESS); + })); + + it('4 following nothing should fail', () => co(function*(){ + const block = { number: 4 }; + const HEAD_1 = null; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(FAIL); + })); + + it('4 following 2 should fail', () => co(function*(){ + const block = { number: 4 }; + const HEAD_1 = { number: 2 }; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(FAIL); + })); + + it('4 following 3 should succeed', () => co(function*(){ + const block = { number: 4 }; + const HEAD_1 = { number: 3 }; + const HEAD = {}; + indexer.prepareNumber(HEAD, HEAD_1); + indexer.ruleNumber(block, HEAD).should.equal(SUCCESS); + })); + +}); diff --git a/test/fast/database.js b/test/fast/database.js index bfa47986b358f2f759d760d663a1cb56c5c04284..f761b82953ac3178421ce9dbd2b9bea514186de4 100644 --- a/test/fast/database.js +++ b/test/fast/database.js @@ -38,7 +38,7 @@ describe("SQLite driver", function() { yield driver.closeConnection(); yield driver.executeAll(SELECT_FROM_TABLE, []) .then(() => 'Should have thrown an exception') - .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/)); + .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/)); // But if we populate it again, it will work yield driver.executeSql(CREATE_TABLE_SQL); rows = yield driver.executeAll(SELECT_FROM_TABLE, []); @@ -48,7 +48,7 @@ describe("SQLite driver", function() { yield driver.destroyDatabase(); yield driver.executeAll(SELECT_FROM_TABLE, []) .then(() => 'Should have thrown an exception') - .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/)); + .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/)); // But if we populate it again, it will work yield driver.executeSql(CREATE_TABLE_SQL); rows = yield driver.executeAll(SELECT_FROM_TABLE, []); @@ -79,7 +79,7 @@ describe("SQLite driver", function() { yield driver.destroyDatabase(); yield driver.executeAll(SELECT_FROM_TABLE, []) .then(() => 'Should have thrown an exception') - .catch((err) => err.should.have.property('message').match(/^SQLITE_ERROR: no such table: duniter$/)); + .catch((err) => err.should.have.property('message').match(/SQLITE_ERROR: no such table: duniter/)); })); it('should be able to open the file after being removed', () => co(function*() { diff --git a/test/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js new file mode 100644 index 0000000000000000000000000000000000000000..87402da287adadb9d3c2b233fb38bdb6bfa0c6ea --- /dev/null +++ b/test/fast/v1.0-local-index.js @@ -0,0 +1,154 @@ +"use strict"; + +const _ = require('underscore'); +const should = require('should'); +const parsers = require('../../app/lib/streams/parsers'); +const indexer = require('../../app/lib/dup/indexer'); +const constants = require('../../app/lib/constants'); + +const raw = "Version: 5\n" + + "Type: Block\n" + + "Currency: beta_brousouf\n" + + "Number: 10\n" + + "PoWMin: 1\n" + + "Time: 1411785481\n" + + "MedianTime: 1411776000\n" + + "UnitBase: 2\n" + + "Issuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + + "IssuersFrame: 100\n" + + "IssuersFrameVar: 0\n" + + "DifferentIssuersCount: 3\n" + + "PreviousHash: 2A27BD040B16B7AF59DDD88890E616987F4DD28AA47B9ABDBBEE46257B88E945\n" + + "PreviousIssuer: HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd\n" + + "MembersCount: 3\n" + + "Identities:\n" + + "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat\n" + + "Joiners:\n" + + "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:iSQvl1VVc6+b1AUaBJ/VTTurGGHgaIcjASBhIlzI7M/7KVQV2Wi3oGUZUzLWqCAtGUsPcsj1HCV2/sRyxHmqAw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cat\n" + + "65dKz7JEvZzy6Znr9hATtvm7Kd9fCwxhWKgyrbyL2jhX:25xK7+ph7IYeN9Hu8PvuIBjYdVURYtvKayPHZg7zrrYTs6ii2fMtk5J65a3bT/NKr2Qsd7I5TCL29QyiAXa7BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:tac\n" + + "Actives:\n" + + "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:ze+ftHWFLYmjfvXyrx4a15N2VQjf6oen8kkMiYNYrVllbpb5IUcb28CenlOQbVd9cZCNGSkTP7xP5bt8KAqUAw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:toc\n" + + "Leavers:\n" + + "HEgBcwtkrnWBgwDqELYht6aBZrmjm8jQY4DtFRjcB437:25xK7+ph7IYeN9Hu8PvuIBjYdVURYtvKayPHZg7zrrYTs6ii2fMtk5J65a3bT/NKr2Qsd7I5TCL29QyiAXa7BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:tac\n" + + "Revoked:\n" + + "EKWJvwPaYuLTv1VoCEtZLmtUTxTC5gWVfdWeRgKgZChN:iSQvl1VVc6+b1AUaBJ/VTTurGGHgaIcjASBhIlzI7M/7KVQV2Wi3oGUZUzLWqCAtGUsPcsj1HCV2/sRyxHmqAw==\n" + + "Excluded:\n" + + "EKWJvwPaYuLTv1VoCEtZLmtUTxTC5gWVfdWeRgKgZChN\n" + + "BNmj8fnZuDtpvismiWnFneJkPHpB98bZdc5ozNYzBW78\n" + + "Certifications:\n" + + "G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:0:CK6UDDJM3d0weE1RVtzFJnw/+J507lPAtspleHc59T4+N1tzQj1RRGWrzPiTknCjnCO6SxBSJX0B+MIUWrpNAw==\n" + + "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:0:a7SFapoVaXq27NU+wZj4afmxp0SbwLGqLJih8pfX6TRKPvNp/V93fbKixbqg10cwa1CadNenztxq3ZgOivqADw==\n" + + "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:0:bJyoM2Tz4hltVXkLvYHOOmLP4qqh2fx7aMLkS5q0cMoEg5AFER3iETj13uoFyhz8yiAKESyAZSDjjQwp8A1QDw==\n" + + "F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:0:h8D/dx/z5K2dx06ktp7fnmLRdxkdV5wRkJgnmEvKy2k55mM2RyREpHfD7t/1CC5Ew+UD0V9N27PfaoLxZc1KCQ==\n" + + "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:F5PtTpt8QFYMGtpZaETygB2C2yxCSxH1UW1VopBNZ6qg:0:eefk9Gg0Ijz0GvrNnRc55CCCBd4yk8j0fNzWzVZFKR3kZ7lsKav6dWyAsaVhlNG5S6XwEwvPoMwKJq1Vn7OjBg==\n" + + "Transactions:\n" + + "TX:3:1:6:6:8:1:0\n" + + "33753-0000054FC8AC7B450BA7D8BA7ED873FEDD5BF1E98D5D3B0DEE38DED55CB80CB3\n" + + "G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU\n" + + "150605:3:T:01B1AB40E7C1021712FF40D5605037C0ACEECA547BF519ABDCB6473A9F6BDF45:1\n" + + "297705:3:D:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:33530\n" + + "2244725:3:T:507CBE120DB654645B55431A9967789ACB7CD260EA962B839F1708834D1E5491:0\n" + + "972091:2:D:G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU:30324\n" + + "3808457:2:T:657229C5433FB9FFE64BF2E795E79DA796E0B1AF536DC740ECC26CCBBE104C33:1\n" + + "4:2:T:507CBE120DB654645B55431A9967789ACB7CD260EA962B839F1708834D1E5491:1\n" + + "0:SIG(0)\n" + + "1:SIG(0)\n" + + "2:SIG(0)\n" + + "3:SIG(0)\n" + + "4:SIG(0)\n" + + "5:SIG(0)\n" + + "3171064:3:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" + + "3:2:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" + + "4:1:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" + + "8:0:SIG(5ocqzyDMMWf1V8bsoNhWb1iNwax1e9M7VTUN6navs8of)\n" + + "25:3:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" + + "8:2:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" + + "5:1:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" + + "2:0:SIG(G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU)\n" + + "all 10.6517\n" + + "42yQm4hGTJYWkPg39hQAUgP6S6EQ4vTfXdJuxKEHL1ih6YHiDL2hcwrFgBHjXLRgxRhj2VNVqqc6b4JayKqTE14r\n" + + "TX:3:1:1:1:1:0:0\n" + + "5-2C31D8915801E759F6D4FF3DA8DA983D7D56DCF4F8D94619FCFAD4B128362326\n" + + "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY\n" + + "10:3:T:2C31D8915801E759F6D4FF3DA8DA983D7D56DCF4F8D94619FCFAD4B128362326:88\n" + + "0:SIG(0)\n" + + "1:4:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)\n" + + "I6gJkJIQJ9vwDRXZ6kdBsOArQ3zzMYPmFxDbJqseBVq5NWlmJ7l7oY9iWtqhPF38rp7/iitbgyftsRR8djOGDg==\n" + + "InnerHash: DE837CA3F49C423A6A6C124819ABA31A41C1C4A4E2728B5721DF891B98FA8D0D\n" + + "Nonce: 1\n" + + "kNsKdC8eH0d4zdHh1djyMzRXjFrwk3Bc3M8wo4DV/7clE9J66K/U0FljyS79SI78ZZUPaVmrImKJ9SNiubCiBg==\n"; + +describe("v1.0 Local Index", function(){ + + const block = parsers.parseBlock.syncWrite(raw); + const index = indexer.localIndex(block, { sigValidity: 100, msValidity: 40 }); + + it('should have 30 index entries', () => { + index.should.have.length(30); + }); + + /********* + * IINDEX + ********/ + + it('should have 4 iindex entries', () => { + _(index).where({ index: constants.I_INDEX}).should.have.length(4); + }); + + it('should have 1 iindex CREATE entries', () => { + _(index).where({ index: constants.I_INDEX, op: constants.IDX_CREATE }).should.have.length(1); + }); + + it('should have 3 iindex UPDATE entries', () => { + _(index).where({ index: constants.I_INDEX, op: constants.IDX_UPDATE }).should.have.length(3); + }); + + /********* + * MINDEX + ********/ + + it('should have 5 mindex entries', () => { + _(index).where({ index: constants.M_INDEX}).should.have.length(5); + }); + + it('should have 1 mindex CREATE entries', () => { + _(index).where({ index: constants.M_INDEX, op: constants.IDX_CREATE }).should.have.length(1); + }); + + it('should have 4 mindex UPDATE entries', () => { + _(index).where({ index: constants.M_INDEX, op: constants.IDX_UPDATE }).should.have.length(4); + }); + + /********* + * CINDEX + ********/ + + it('should have 5 cindex entries', () => { + _(index).where({ index: constants.C_INDEX}).should.have.length(5); + }); + + it('should have 5 cindex CREATE entries', () => { + _(index).where({ index: constants.C_INDEX, op: constants.IDX_CREATE }).should.have.length(5); + }); + + it('should have 0 cindex UPDATE entries', () => { + _(index).where({ index: constants.C_INDEX, op: constants.IDX_UPDATE }).should.have.length(0); + }); + + /********* + * SINDEX + ********/ + + it('should have 16 cindex entries', () => { + _(index).where({ index: constants.S_INDEX}).should.have.length(16); + }); + + it('should have 9 cindex CREATE entries', () => { + _(index).where({ index: constants.S_INDEX, op: constants.IDX_CREATE }).should.have.length(9); + }); + + it('should have 7 cindex UPDATE entries', () => { + _(index).where({ index: constants.S_INDEX, op: constants.IDX_UPDATE }).should.have.length(7); + }); + +}); diff --git a/test/integration/branches_pending_data.js b/test/integration/branches_pending_data.js index 8b4fbb51aa054fb257cd1c875fd9a8ca7cef3ea3..5eb7f35da4cb08801c18a44341917d258dd6f765 100644 --- a/test/integration/branches_pending_data.js +++ b/test/integration/branches_pending_data.js @@ -2,7 +2,7 @@ const co = require('co'); const _ = require('underscore'); -const ucoin = require('../../index'); +const duniter = require('../../index'); const bma = require('../../app/lib/streams/bma'); const user = require('./tools/user'); const rp = require('request-promise'); @@ -22,7 +22,7 @@ const commonConf = { sigQty: 1 }; -const s1 = ucoin({ +const s1 = duniter({ memory: MEMORY_MODE, name: 'bb6' }, _.extend({ diff --git a/test/integration/branches_revert.js b/test/integration/branches_revert.js index fee75afc00b922b5646b7738ddc3d82269b2e8ac..066b05ad5226add5711016dd69dbf9d37d8c9a54 100644 --- a/test/integration/branches_revert.js +++ b/test/integration/branches_revert.js @@ -2,17 +2,11 @@ const co = require('co'); const _ = require('underscore'); -const ucoin = require('../../index'); const bma = require('../../app/lib/streams/bma'); const user = require('./tools/user'); -const rp = require('request-promise'); -const httpTest = require('./tools/http'); +const toolbox = require('./tools/toolbox'); const commit = require('./tools/commit'); -const expectJSON = httpTest.expectJSON; -const expectHttpCode = httpTest.expectHttpCode; - -const MEMORY_MODE = true; const commonConf = { ipv4: '127.0.0.1', currency: 'bb', @@ -22,17 +16,13 @@ const commonConf = { sigQty: 1 }; -const s1 = ucoin({ - memory: MEMORY_MODE, - name: 'bb11' -}, _.extend({ - port: '7711', +const s1 = toolbox.server(_.extend({ pair: { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 1, ud0: 120 }, commonConf)); const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); @@ -51,36 +41,24 @@ describe("Revert root", function() { yield cat.join(); yield toc.join(); yield commit(s1)(); - yield s1.revert(); - yield commit(s1)(); - yield commit(s1)(); }); }); - describe("Server 1 /blockchain", function() { - - it('/block/0 should exist', function() { - return expectJSON(rp('http://127.0.0.1:7711/blockchain/block/0', { json: true }), { - number: 0 - }); - }); - - it('/block/1 should exist', function() { - return expectJSON(rp('http://127.0.0.1:7711/blockchain/block/1', { json: true }), { - number: 1 - }); - }); + it('/block/0 should exist', () => s1.expectJSON('/blockchain/block/0', { + number: 0 + })); - it('/block/2 should exist', function() { - return httpTest.expectError(404, 'Block not found', rp('http://127.0.0.1:7711/blockchain/block/2', { json: true })); - }); + it('/wot/cat should exist', () => s1.expectThat('/wot/lookup/cat', (res) => { + res.should.have.property('results').length(1); + res.results[0].should.have.property('uids').length(1); + res.results[0].uids[0].should.have.property('uid').equal('cat'); + res.results[0].uids[0].should.have.property('others').length(1); + })); - // Revert then Commit should be a neutral operation - - //it('should conserve newcomers', function() {}); - //it('should conserve identity joining back', function() {}); - //it('should conserve identity active', function() {}); - //it('should conserve identity leaving', function() {}); - //it('should conserve excluded', function() {}); - }); + it('reverting should erase everything', () => co(function*() { + yield s1.revert(); + yield s1.expectError('/blockchain/current', 404, 'No current block'); + yield s1.expectError('/blockchain/block/0', 404, 'Block not found'); + yield s1.expectError('/wot/lookup/cat', 404, 'No matching identity'); // Revert completely removes the identity + })); }); diff --git a/test/integration/branches_revert2.js b/test/integration/branches_revert2.js index 04a8529cbb085550c8b756b2281e505507391a40..c364c848ba0c485493f86610e9483b028cb4f920 100644 --- a/test/integration/branches_revert2.js +++ b/test/integration/branches_revert2.js @@ -33,7 +33,7 @@ const s1 = ucoin({ sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 1, ud0: 120 }, commonConf)); const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); @@ -41,6 +41,8 @@ const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', s describe("Revert two blocks", function() { + const now = Math.floor(Date.now() / 1000); + before(function() { return co(function *() { @@ -51,10 +53,11 @@ describe("Revert two blocks", function() { yield cat.cert(toc); yield cat.join(); yield toc.join(); - yield commit(s1)({ version: 2 }); - yield commit(s1)({ version: 2 }); + yield commit(s1)({ version: 2, time: now }); + yield commit(s1)({ version: 2, time: now + 10 }); + yield commit(s1)({ version: 2, time: now + 10 }); yield cat.sendP(51, toc); - yield commit(s1)({ version: 2 }); + yield commit(s1)({ version: 2, time: now + 10 }); yield s1.revert(); }); }); @@ -68,8 +71,8 @@ describe("Revert two blocks", function() { }); it('/block/1 should exist', function() { - return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/1', { json: true }), { - number: 1, + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/2', { json: true }), { + number: 2, dividend: 120 }); }); @@ -80,7 +83,7 @@ describe("Revert two blocks", function() { res.sources.should.have.length(1); res.sources[0].should.have.property('identifier').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); res.sources[0].should.have.property('type').equal('D'); - res.sources[0].should.have.property('noffset').equal(1); + res.sources[0].should.have.property('noffset').equal(2); res.sources[0].should.have.property('amount').equal(120); }); }); @@ -91,7 +94,7 @@ describe("Revert two blocks", function() { res.sources.should.have.length(1); res.sources[0].should.have.property('identifier').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'); res.sources[0].should.have.property('type').equal('D'); - res.sources[0].should.have.property('noffset').equal(1); + res.sources[0].should.have.property('noffset').equal(2); res.sources[0].should.have.property('amount').equal(120); }); }); diff --git a/test/integration/branches_revert_memberships.js b/test/integration/branches_revert_memberships.js index 2ae7efc431cfd1e722400c2bbfdec99483de24ad..35479468077a5d325d0b67825569f710ebc887c2 100644 --- a/test/integration/branches_revert_memberships.js +++ b/test/integration/branches_revert_memberships.js @@ -5,7 +5,6 @@ const should = require('should'); const bma = require('../../app/lib/streams/bma'); const user = require('./tools/user'); const commit = require('./tools/commit'); -const until = require('./tools/until'); const toolbox = require('./tools/toolbox'); const limiter = require('../../app/lib/system/limiter'); const multicaster = require('../../app/lib/streams/multicaster'); @@ -25,7 +24,7 @@ const i3 = user('i3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', s describe("Revert memberships", function() { - const now = Math.round(Date.now() / 1000); + const now = 1482000000; before(() => co(function*() { @@ -106,7 +105,7 @@ describe("Revert memberships", function() { })); it('should exist a kicked member', () => co(function*() { - yield s1.commit({ time: now + 55 }); + yield s1.commit({ time: now + 25 }); // yield s1.expect('/blockchain/current', (res) => { res.number.should.equal(7); (res.medianTime - now).should.equal(25); }); // yield s1.commit({ time: now + 30 }); // yield s1.expect('/blockchain/current', (res) => { res.number.should.equal(8); (res.medianTime - now).should.equal(18); }); @@ -133,46 +132,45 @@ describe("Revert memberships", function() { it('revert the join back', () => co(function*() { yield s1.revert(); yield shouldHaveBeenKicked(); - yield shouldHavePendingMS(1); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert excluded member', () => co(function*() { yield s1.revert(); yield shouldBeBeingKicked(); - yield shouldHavePendingMS(1); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert being kicked', () => co(function*() { yield s1.revert(); yield shouldBeLeaving(); - yield shouldHavePendingMS(1); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert leaving', () => co(function*() { yield s1.revert(); yield shouldBeRenewed(); - yield shouldHavePendingMS(2); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert 2 neutral blocks for i3', () => co(function*() { yield s1.revert(); yield shouldBeRenewed(); - yield shouldHavePendingMS(4); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost yield s1.revert(); yield shouldBeRenewed(); - yield shouldHavePendingMS(4); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert renewal block', () => co(function*() { yield s1.revert(); yield shouldHaveJoined(); - yield shouldHavePendingMS(5); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); it('revert join block', () => co(function*() { yield s1.revert(); - yield shouldBeJoining(); - yield shouldHavePendingMS(6); // Keep track of undone membership + yield shouldHavePendingMS(0); // Undone memberships are lost })); /********* @@ -192,13 +190,10 @@ describe("Revert memberships", function() { function shouldBeFreshlyCreated() { return co(function*() { const idty = (yield s1.dal.idtyDAL.searchThoseMatching(i3.pub))[0]; - idty.should.have.property('currentMSN').equal(-1); - idty.should.have.property('currentINN').equal(-1); idty.should.have.property('wasMember').equal(false); idty.should.have.property('written').equal(false); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(false); - idty.should.have.property('leaving').equal(false); }); } @@ -208,80 +203,56 @@ describe("Revert memberships", function() { function shouldHaveJoined() { return co(function*() { - const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(1); - idty.should.have.property('currentINN').equal(1); + const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(true); - idty.should.have.property('leaving').equal(false); }); } function shouldBeRenewed() { return co(function*() { - const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(2); - idty.should.have.property('currentINN').equal(2); + const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(true); - idty.should.have.property('leaving').equal(false); }); } function shouldBeLeaving() { return co(function*() { - const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(5); - idty.should.have.property('currentINN').equal(2); + const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(true); - idty.should.have.property('leaving').equal(true); }); } function shouldBeBeingKicked() { return co(function*() { - // Should be set as kicked bow - const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(5); - idty.should.have.property('currentINN').equal(2); + // Should be set as kicked now + const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(true); idty.should.have.property('member').equal(true); - idty.should.have.property('leaving').equal(true); }); } function shouldHaveBeenKicked() { return co(function*() { - const idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(5); - idty.should.have.property('currentINN').equal(2); + const idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(false); - idty.should.have.property('leaving').equal(true); }); } function shouldHaveComeBack() { return co(function*() { - let idty = yield s1.dal.idtyDAL.getFromPubkey(i3.pub); - idty.should.have.property('currentMSN').equal(8); - idty.should.have.property('currentINN').equal(8); + let idty = yield s1.dal.iindexDAL.getFromPubkey(i3.pub); idty.should.have.property('wasMember').equal(true); - idty.should.have.property('written').equal(true); idty.should.have.property('kick').equal(false); idty.should.have.property('member').equal(true); - idty.should.have.property('leaving').equal(false); }); } }); diff --git a/test/integration/branches_switch.js b/test/integration/branches_switch.js index 22a0bac6c0e0d693e47faadb3acd57b98f70ffb4..c16b1ec12a6f00e3190c1b99c1ca6226c85bd014 100644 --- a/test/integration/branches_switch.js +++ b/test/integration/branches_switch.js @@ -34,7 +34,7 @@ const s1 = ucoin({ sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 1, ud0: 120 }, commonConf)); const s2 = ucoin({ @@ -105,18 +105,5 @@ describe("Switch", function() { number: 8 }); }); - - it('/block/7 should have valid monetary mass', function() { - return co(function *() { - let block = yield s1.dal.getBlock(7); - block.should.have.property('UDTime').not.equal(null); - }); - }); - - it('/block/8 should have valid monetary mass', () => co(function *() { - let block = yield s1.dal.getBlock(8); - block.should.have.property('UDTime').not.equal(null); - }) - ); }); }); diff --git a/test/integration/certification_chainability.js b/test/integration/certification_chainability.js index 5497764141be892c303ebbd2cd47f095ab5c53ba..4fa896612010befedfa7a24d242b115ea0c85977 100644 --- a/test/integration/certification_chainability.js +++ b/test/integration/certification_chainability.js @@ -44,7 +44,7 @@ describe("Certification chainability", function() { before(function() { - const now = Math.round(new Date().getTime() / 1000); + const now = 1482220000; const commitS1 = commit(s1); diff --git a/test/integration/collapse.js b/test/integration/collapse.js index 8a49e456724c899fac222cd3b73e19ba78d07fa2..79878d625a71ab4e63421e908469a623c3bd26b1 100644 --- a/test/integration/collapse.js +++ b/test/integration/collapse.js @@ -29,27 +29,36 @@ const s1 = ucoin({ sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 100, ud0: 120, sigValidity: 1 }, commonConf)); const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 }); +const tac = user('tac', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); describe("Community collapse", function() { + const now = Math.round(Date.now() / 1000); + before(function() { return co(function *() { yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections()); yield cat.createIdentity(); + yield tac.createIdentity(); yield cat.join(); - yield commit(s1)(); - yield commit(s1)(); + yield tac.join(); + yield cat.cert(tac); + yield tac.cert(cat); + yield commit(s1)({ time: now }); + yield commit(s1)({ time: now + 10 }); + yield commit(s1)({ time: now + 10 }); + yield commit(s1)({ time: now + 10 }); }); }); it('should be handled', function() { - return httpTest.expectJSON(rp('http://127.0.0.1:9340/blockchain/block/1', { json: true }), { - number: 1 + return httpTest.expectJSON(rp('http://127.0.0.1:9340/blockchain/block/2', { json: true }), { + number: 2 }); }); }); diff --git a/test/integration/crosschain-test.js b/test/integration/crosschain-test.js index 31302c73425bff47ac3337515c0849a8de2d8127..400532538ac2a39b9634e70d38bc22d3bfa9476d 100644 --- a/test/integration/crosschain-test.js +++ b/test/integration/crosschain-test.js @@ -14,12 +14,14 @@ const httpTest = require('./tools/http'); describe("Crosschain transactions", function() { + const now = Math.round(Date.now() / 1000); + const MEMORY_MODE = true; const commonConf = { ipv4: '127.0.0.1', httpLogs: true, forksize: 3, - dt: 0, ud0: 120, rootoffset: 10, + dt: 1, ud0: 120, rootoffset: 10, parcatipate: false, // TODO: to remove when startGeneration will be an explicit call sigQty: 1 }; @@ -70,14 +72,15 @@ describe("Crosschain transactions", function() { yield ticB.cert(tocB); yield ticB.join(); yield tocB.join(); - yield commit(sB)({ version: 2 }); - yield commit(sB)({ version: 2 }); + yield commit(sB)({ version: 2, time: now }); + yield commit(sB)({ version: 2, time: now + 10 }); + yield commit(sB)({ version: 2, time: now + 10 }); // Preparation: we create a source transaction for our transfer btx0 = yield tocB.prepareITX(120, tocB); // We submit it to the network yield tocB.sendTX(btx0); // Written - yield commit(sB)({ version: 2 }); + yield commit(sB)({ version: 2, time: now + 10 }); // Initialize META yield ticM.createIdentity(); @@ -86,8 +89,9 @@ describe("Crosschain transactions", function() { yield ticM.cert(tocM); yield ticM.join(); yield tocM.join(); - yield commit(sM)({ version: 2 }); - yield commit(sM)({ version: 2 }); + yield commit(sM)({ version: 2, time: now }); + yield commit(sM)({ version: 2, time: now + 10 }); + yield commit(sM)({ version: 2, time: now + 10 }); // Preparation: we create a source transaction for our transfer mtx0 = yield ticM.prepareITX(120, ticM); // We submit it to the network @@ -241,8 +245,9 @@ describe("Crosschain transactions", function() { yield ticB.cert(tocB); yield ticB.join(); yield tocB.join(); - yield commit(sB)({ version: 2 }); - yield commit(sB)({ version: 2 }); + yield commit(sB)({ version: 2, time: now }); + yield commit(sB)({ version: 2, time: now + 10 }); + yield commit(sB)({ version: 2, time: now + 10 }); // Preparation: we create a source transaction for our transfer btx0 = yield tocB.prepareITX(120, tocB); // We submit it to the network @@ -257,8 +262,9 @@ describe("Crosschain transactions", function() { yield ticM.cert(tocM); yield ticM.join(); yield tocM.join(); - yield commit(sM)({ version: 2 }); - yield commit(sM)({ version: 2 }); + yield commit(sM)({ version: 2, time: now }); + yield commit(sM)({ version: 2, time: now + 10 }); + yield commit(sM)({ version: 2, time: now + 10 }); // Preparation: we create a source transaction for our transfer mtx0 = yield ticM.prepareITX(120, ticM); // We submit it to the network diff --git a/test/integration/forwarding.js b/test/integration/forwarding.js index 1fd477872f5b02712cf3c85d03196c6e8bf5a23b..b6ada196caa15f2270d97b98486eb578dcfa9b96 100644 --- a/test/integration/forwarding.js +++ b/test/integration/forwarding.js @@ -27,60 +27,47 @@ describe("Forwarding", function() { const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1); const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1); - before(function(done) { - Promise.all([node1, node2].map(function(node) { - return node - .startTesting(); - })) - .then(function() { - return new Promise(function(resolve, reject){ - async.waterfall([ - function(next) { - node2.peering(next); - }, - function(peer, next) { - node1.submitPeer(peer, function(err) { - next(err); - }); - }, - function(next) { - node1.peering(next); - }, - function(peer, next) { - node2.submitPeer(peer, next); - } - ], function(err) { - err ? reject(err) : resolve(); + before(() => co(function*(){ + yield [node1, node2].map((node) => node.startTesting()); + yield new Promise(function(resolve, reject){ + async.waterfall([ + function(next) { + node2.peering(next); + }, + function(peer, next) { + node1.submitPeer(peer, function(err) { + next(err); }); - }); - }) - .then(function(){ - return Promise.all([ - node2.until('identity', 4), - node2.until('block', 1), - co(function *() { - - // Self certifications - yield cat.createIdentity(); - yield tac.createIdentity(); - yield tic.createIdentity(); - yield toc.createIdentity(); - // Certifications - yield cat.cert(tac); - yield tac.cert(cat); - yield cat.join(); - yield tac.join(); - yield node1.commitP(); - }) - ]); - }) - .then(function(){ - done(); - }) - .catch(function(err){ - done(err); + }, + function(next) { + node1.peering(next); + }, + function(peer, next) { + node2.submitPeer(peer, next); + } + ], function(err) { + err ? reject(err) : resolve(); }); - }); + }); + yield [ + node2.until('identity', 4), + node2.until('block', 1), + co(function *() { + + // Self certifications + yield cat.createIdentity(); + yield tac.createIdentity(); + yield tic.createIdentity(); + yield toc.createIdentity(); + // Certifications + yield cat.cert(tac); + yield tac.cert(cat); + yield cat.join(); + yield tac.join(); + yield node1.commitP(); + }) + ]; + })); describe("Testing technical API", function(){ @@ -118,52 +105,70 @@ function doTests(node) { describe("user cat", function(){ it('should give only 1 result', node.lookup('cat', function(res, done){ - should.exists(res); - assert.equal(res.results.length, 1); - done(); + try { + should.exists(res); + assert.equal(res.results.length, 1); + done(); + } catch (e) { + done(e); + } })); it('should have sent 1 signature', node.lookup('cat', function(res, done){ - should.exists(res); - assert.equal(res.results[0].signed.length, 1); - should.exists(res.results[0].signed[0].isMember); - should.exists(res.results[0].signed[0].wasMember); - assert.equal(res.results[0].signed[0].isMember, true); - assert.equal(res.results[0].signed[0].wasMember, true); - done(); + try { + should.exists(res); + assert.equal(res.results[0].signed.length, 1); + should.exists(res.results[0].signed[0].isMember); + should.exists(res.results[0].signed[0].wasMember); + assert.equal(res.results[0].signed[0].isMember, true); + assert.equal(res.results[0].signed[0].wasMember, true); + done(); + } catch (e) { + done(e); + } })); }); describe("user tac", function(){ it('should give only 1 result', node.lookup('tac', function(res, done){ - should.exists(res); - assert.equal(res.results.length, 1); - done(); + try { + should.exists(res); + assert.equal(res.results.length, 1); + done(); + } catch (e) { + done(e); + } })); it('should have 1 signature', node.lookup('tac', function(res, done){ - should.exists(res); - assert.equal(res.results[0].uids[0].others.length, 1); - done(); + try { + should.exists(res); + assert.equal(res.results[0].uids[0].others.length, 1); + done(); + } catch (e) { + done(e); + } })); it('should have sent 1 signature', node.lookup('tac', function(res, done){ - should.exists(res); - assert.equal(res.results[0].signed.length, 1); - done(); + try { + should.exists(res); + assert.equal(res.results[0].signed.length, 1); + done(); + } catch (e) { + done(e); + } })); }); it('toc should give only 1 result', node.lookup('toc', function(res, done){ - should.exists(res); - assert.equal(res.results.length, 1); + should.not.exists(res); done(); })); it('tic should give only 1 results', node.lookup('tic', function(res, done){ - should.exists(res); - assert.equal(res.results.length, 1); + should.not.exists(res); done(); })); }; diff --git a/test/integration/identity-expiry.js b/test/integration/identity-expiry.js index 2b5c8b35401588a62dc56db25205b7134f73f3b6..6a0a67191ce180ac42e2328796a6407d9432b57f 100644 --- a/test/integration/identity-expiry.js +++ b/test/integration/identity-expiry.js @@ -11,7 +11,8 @@ const rp = require('request-promise'); const httpTest = require('./tools/http'); const commit = require('./tools/commit'); -const expectAnswer = httpTest.expectAnswer; +const expectAnswer = httpTest.expectAnswer; +const expectError = httpTest.expectError; const MEMORY_MODE = true; const commonConf = { @@ -42,15 +43,15 @@ const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', s const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 }); const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 }); +const now = 1482300000; +const commitS1 = commit(s1); + describe("Identities expiry", function() { before(function() { - const commitS1 = commit(s1); - return co(function *() { - const now = Math.round(new Date().getTime() / 1000); yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections()); yield cat.createIdentity(); yield tac.createIdentity(); @@ -59,25 +60,20 @@ describe("Identities expiry", function() { yield tac.cert(cat); yield cat.join(); yield tac.join(); - yield commitS1(); - yield toc.createIdentity(); - yield toc.join(); yield commitS1({ - time: now + 10 + time: now }); + yield toc.createIdentity(); + yield toc.join(); yield commitS1({ - time: now + 10 + time: now + 5 }); }); }); it('should have requirements failing for tic', function() { - return expectAnswer(rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true }), (res) => { - res.should.have.property('identities').length(1); - res.identities[0].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); - res.identities[0].should.have.property('uid').equal('tic'); - res.identities[0].should.have.property('expired').equal(true); - }); + // tic has been cleaned up, since its identity has expired after the root block + return expectError(404, 'No identity matching this pubkey or uid', rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true })); }); it('should have requirements failing for toc', function() { @@ -85,7 +81,15 @@ describe("Identities expiry", function() { res.should.have.property('identities').length(1); res.identities[0].should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'); res.identities[0].should.have.property('uid').equal('toc'); - res.identities[0].should.have.property('expired').equal(true); + res.identities[0].should.have.property('expired').equal(false); }); }); + + it('should have requirements failing for toc', () => co(function*() { + // tic has been cleaned up after the block#2 + yield commitS1({ + time: now + 5 + }); + return expectError(404, 'No identity matching this pubkey or uid', rp('http://127.0.0.1:8560/wot/requirements/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', { json: true })); + })); }); diff --git a/test/integration/identity-test.js b/test/integration/identity-test.js index 036ec9dc60edb4f25be96ee40aaf7f7b4f277b4d..bb0a217a2375e3cb7bca51087345453b870d5c2d 100644 --- a/test/integration/identity-test.js +++ b/test/integration/identity-test.js @@ -283,26 +283,30 @@ describe("Identities collision", function() { res.should.have.property('sigDate').be.a.Number; res.should.have.property('certifications').length(2); let certs = res.certifications; - certs[0].should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'); - certs[0].should.have.property('uid').equal('toc'); - certs[0].should.have.property('isMember').equal(true); - certs[0].should.have.property('wasMember').equal(true); - certs[0].should.have.property('sigDate').be.a.Number; - certs[0].should.have.property('cert_time').property('block').be.a.Number; - certs[0].should.have.property('cert_time').property('medianTime').be.a.Number; - certs[0].should.have.property('written').property('number').equal(0); - certs[0].should.have.property('written').property('hash').not.equal(''); - certs[0].should.have.property('signature').not.equal(''); - certs[1].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); - certs[1].should.have.property('uid').equal('tic'); - certs[1].should.have.property('isMember').equal(true); - certs[1].should.have.property('wasMember').equal(true); - certs[1].should.have.property('sigDate').be.a.Number; - certs[1].should.have.property('cert_time').property('block').be.a.Number; - certs[1].should.have.property('cert_time').property('medianTime').be.a.Number; - certs[1].should.have.property('written').property('number').equal(0); - certs[1].should.have.property('written').property('hash').not.equal(''); - certs[1].should.have.property('signature').not.equal(''); + for (const cert of certs) { + if (cert.pubkey == 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo') { + cert.should.have.property('uid').equal('toc'); + cert.should.have.property('isMember').equal(true); + cert.should.have.property('wasMember').equal(true); + cert.should.have.property('sigDate').be.a.Number; + cert.should.have.property('cert_time').property('block').be.a.Number; + cert.should.have.property('cert_time').property('medianTime').be.a.Number; + cert.should.have.property('written').property('number').equal(0); + cert.should.have.property('written').property('hash').not.equal(''); + cert.should.have.property('signature').not.equal(''); + } else { + cert.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); + cert.should.have.property('uid').equal('tic'); + cert.should.have.property('isMember').equal(true); + cert.should.have.property('wasMember').equal(true); + cert.should.have.property('sigDate').be.a.Number; + cert.should.have.property('cert_time').property('block').be.a.Number; + cert.should.have.property('cert_time').property('medianTime').be.a.Number; + cert.should.have.property('written').property('number').equal(0); + cert.should.have.property('written').property('hash').not.equal(''); + cert.should.have.property('signature').not.equal(''); + } + } }); }); @@ -341,34 +345,27 @@ describe("Identities collision", function() { res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); res.should.have.property('uid').equal('tic'); res.should.have.property('sigDate').be.a.Number; - res.should.have.property('memberships').length(2); - // Initial membership + res.should.have.property('memberships').length(1); // We no more conserve the memberships in sandbox + // Renew membership, not written res.memberships[0].should.have.property('version').equal(constants.DOCUMENTS_VERSION); res.memberships[0].should.have.property('currency').equal('bb'); res.memberships[0].should.have.property('membership').equal('IN'); res.memberships[0].should.have.property('blockNumber').equal(1); res.memberships[0].should.have.property('blockHash').not.equal('E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'); res.memberships[0].should.have.property('written').equal(null); - // Renew membership, not written - res.memberships[1].should.have.property('version').equal(constants.DOCUMENTS_VERSION); - res.memberships[1].should.have.property('currency').equal('bb'); - res.memberships[1].should.have.property('membership').equal('IN'); - res.memberships[1].should.have.property('blockNumber').equal(0); - res.memberships[1].should.have.property('blockHash').equal('E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'); - res.memberships[1].should.have.property('written').equal(0); }); }); - it('memberships of man3', function() { - return httpTest.expectHttpCode(404, rp('http://127.0.0.1:7799/blockchain/memberships/man3')); - }); - - it('difficulties', function() { - return expectAnswer(rp('http://127.0.0.1:7799/blockchain/difficulties', { json: true }), function(res) { - res.should.have.property('block').equal(2); - res.should.have.property('levels').length(1); - res.levels[0].should.have.property('uid').equal('cat'); - res.levels[0].should.have.property('level').equal(4); - }); - }); + // it('memberships of man3', function() { + // return httpTest.expectHttpCode(404, rp('http://127.0.0.1:7799/blockchain/memberships/man3')); + // }); + // + // it('difficulties', function() { + // return expectAnswer(rp('http://127.0.0.1:7799/blockchain/difficulties', { json: true }), function(res) { + // res.should.have.property('block').equal(2); + // res.should.have.property('levels').length(1); + // res.levels[0].should.have.property('uid').equal('cat'); + // res.levels[0].should.have.property('level').equal(4); + // }); + // }); }); diff --git a/test/integration/migrations.js b/test/integration/migrations.js index c91f9c6cb03b9cead7d83e2b4b37f48fc93ef3b1..e10f313f8930cc4263ccb88f82657ff46f2c8a8b 100644 --- a/test/integration/migrations.js +++ b/test/integration/migrations.js @@ -34,7 +34,7 @@ describe("Migration", function() { })); it('should be able to commit the transaction', () => co(function*() { - yield node.commitP(); + yield node.commitP({ time: 1481800000 + 100 }); // The recipients are wrongly valued in this version yield node.server.dal.txsDAL.exec('UPDATE txs SET recipients = "[]";'); })); diff --git a/test/integration/revocation-test.js b/test/integration/revocation-test.js index c6f4c89f33b6ecbbbf559c11668a497a0add3561..b6f0dc8ea7d7af9541926783fde981eb7165e1ed 100644 --- a/test/integration/revocation-test.js +++ b/test/integration/revocation-test.js @@ -2,7 +2,8 @@ const _ = require('underscore'); const co = require('co'); -const ucoin = require('../../index'); +const should = require('should'); +const duniter = require('../../index'); const bma = require('../../app/lib/streams/bma'); const user = require('./tools/user'); const rp = require('request-promise'); @@ -12,7 +13,7 @@ const limiter = require('../../app/lib/system/limiter'); limiter.noLimit(); -const expectAnswer = httpTest.expectAnswer; +const expectAnswer = httpTest.expectAnswer; const MEMORY_MODE = true; const commonConf = { @@ -26,7 +27,7 @@ const commonConf = { sigQty: 1 }; -const s1 = ucoin({ +const s1 = duniter({ memory: MEMORY_MODE, name: 'bb12' }, _.extend({ @@ -37,9 +38,9 @@ const s1 = ucoin({ } }, commonConf)); -const s2 = ucoin({ +const s2 = duniter({ memory: MEMORY_MODE, - name: 'bb12' + name: 'bb13' }, _.extend({ port: '9965', pair: { @@ -149,7 +150,28 @@ describe("Revocation", function() { }); })); + it('should have 2 members', function() { + return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) { + res.should.have.property('results').length(2); + _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']); + }); + }); + + it('cat should not be able to join back', () => co(function *() { + try { + yield cat.join(); + } catch (e) { + should.exists(e); + } + yield commitS1(); + return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) { + res.should.have.property('results').length(2); + _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']); + }); + })); + it('if we revert the commit, cat should not be revoked', () => co(function *() { + yield s1.revert(); yield s1.revert(); return expectAnswer(rp('http://127.0.0.1:9964/wot/lookup/cat', { json: true }), function(res) { res.should.have.property('results').length(1); @@ -157,37 +179,21 @@ describe("Revocation", function() { res.results[0].uids[0].should.have.property('uid').equal('cat'); res.results[0].uids[0].should.have.property('revoked').equal(false); res.results[0].uids[0].should.have.property('revoked_on').equal(null); - res.results[0].uids[0].should.have.property('revocation_sig').not.equal(null); - res.results[0].uids[0].should.have.property('revocation_sig').not.equal(''); + res.results[0].uids[0].should.have.property('revocation_sig').equal(null); // We loose the revocation + res.results[0].uids[0].should.have.property('revocation_sig').equal(null); }); })); - it('if we commit again, cat should be revoked', () => co(function *() { + it('if we commit again, cat should NOT be revoked (we have lost the revocation)', () => co(function *() { yield commitS1(); return expectAnswer(rp('http://127.0.0.1:9964/wot/lookup/cat', { json: true }), function(res) { res.should.have.property('results').length(1); res.results[0].should.have.property('uids').length(1); res.results[0].uids[0].should.have.property('uid').equal('cat'); - res.results[0].uids[0].should.have.property('revoked').equal(true); - res.results[0].uids[0].should.have.property('revoked_on').equal(1); - res.results[0].uids[0].should.have.property('revocation_sig').not.equal(null); - res.results[0].uids[0].should.have.property('revocation_sig').not.equal(''); - }); - })); - - it('should have 2 members', function() { - return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) { - res.should.have.property('results').length(2); - _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']); - }); - }); - - it('cat should not be able to join back', () => co(function *() { - yield cat.join(); - yield commitS1(); - return expectAnswer(rp('http://127.0.0.1:9964/wot/members', { json: true }), function(res) { - res.should.have.property('results').length(2); - _.pluck(res.results, 'uid').sort().should.deepEqual(['tic','toc']); + res.results[0].uids[0].should.have.property('revoked').equal(false); + res.results[0].uids[0].should.have.property('revoked_on').equal(null); + res.results[0].uids[0].should.have.property('revocation_sig').equal(null); // We loose the revocation + res.results[0].uids[0].should.have.property('revocation_sig').equal(null); }); })); diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js index 72f5036b7ecc7e91f2b4ea4a4502bfc9a5bb1a3d..4f423316cdffc8306a53d2fd1ea85ceda2e03c6a 100644 --- a/test/integration/server-sandbox.js +++ b/test/integration/server-sandbox.js @@ -14,7 +14,7 @@ limiter.noLimit(); const s1 = toolbox.server({ idtyWindow: 10, - certWindow: 10, + sigWindow: 10, msWindow: 10, dt: 10, pair: { @@ -57,7 +57,7 @@ const i14 = user('i14', { pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX', s describe("Sandboxes", function() { - const now = parseInt(Date.now() / 1000); + const now = 1482300000; before(() => co(function*() { @@ -101,9 +101,12 @@ describe("Sandboxes", function() { yield i1.join(); yield i4.join(); (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0); - yield s1.commit(); - (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(1); // i2, i3 - yield s1.commit(); + yield s1.commit({ time: now }); + (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(3); // i2, i3 were removed for too old identities (based on virtual root block) + yield i2.createIdentity(); + yield i3.createIdentity(); + (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(1); + yield s1.commit({ time: now }); yield s2.syncFrom(s1, 0, 1); yield s3.syncFrom(s1, 0, 1); })); @@ -121,8 +124,11 @@ describe("Sandboxes", function() { yield i7.revoke(idty); }))); - it('should accept i7(1), i8(1), i9(1) by i1->i7(1), i1->i8(1), i1->i9(1)', () => co(function *() { + it('should reject i1 -> i7 by revocation', () => shouldThrow(co(function *() { yield i1.cert(i7, s2); + }))); + + it('should accept i7(1), i8(1), i9(1) by i1->i7(1), i1->i8(1), i1->i9(1)', () => co(function *() { (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0); yield i8.createIdentity(null, s2); yield i1.cert(i8, s2); @@ -175,6 +181,9 @@ describe("Sandboxes", function() { describe('Certifications', () => { it('should accept i4->i7(0),i4->i8(0),i4->i9(0)', () => co(function *() { + yield i7.createIdentity(); + yield i8.createIdentity(); + yield i9.createIdentity(); s1.dal.certDAL.setSandboxSize(3); (yield s1.dal.certDAL.getSandboxRoom()).should.equal(3); yield i4.cert(i7); @@ -204,6 +213,8 @@ describe("Sandboxes", function() { describe('Memberships', () => { it('should accept i8,i9', () => co(function *() { + yield i8.createIdentity(); // Identities have changed + yield i9.createIdentity(); (yield s1.dal.msDAL.getSandboxRoom()).should.equal(2); yield i8.join(); yield i9.join(); diff --git a/test/integration/tests.js b/test/integration/tests.js index ab2c03cceb13b8e9e7ce1c48f3cb8eb5baa3b729..9b23de8cbf81e8714655b2b737949d80980e0bce 100644 --- a/test/integration/tests.js +++ b/test/integration/tests.js @@ -187,7 +187,7 @@ describe("Integration", function() { return co(function *() { yield node3.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections()); - let now = Math.round(new Date().getTime() / 1000); + const now = 1482220000; // Self certifications yield cat.createIdentity(); @@ -209,9 +209,13 @@ describe("Integration", function() { yield commit(node3)({ time: now }); - yield commit(node3)(); + yield commit(node3)({ + time: now + }); yield toc.leave(); - yield commit(node3)(); + yield commit(node3)({ + time: now + }); yield tac.cert(toc); yield tic.cert(toc); yield toc.cert(tic); // Should be taken in 1 block @@ -222,7 +226,9 @@ describe("Integration", function() { yield commit(node3)({ time: now + 200 }); - yield commit(node3)(); + yield commit(node3)({ + time: now + 200 + }); }); }); @@ -269,8 +275,8 @@ describe("Integration", function() { blockShouldHaveCerts(1, 0); blockShouldHaveCerts(2, 0); blockShouldHaveCerts(3, 1); - blockShouldHaveCerts(4, 0); - blockShouldHaveCerts(5, 1); + blockShouldHaveCerts(4, 1); + blockShouldHaveCerts(5, 0); function blockShouldHaveCerts(number, certificationsCount) { it('it should exist block#' + number + ' with ' + certificationsCount + ' certification', () => expectAnswer(rp('http://127.0.0.1:9997/blockchain/block/' + number, { json: true }), function(block) { diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js index c3c18d68093f336927985bf50247d138d4b41ea1..d5f787bd35b7115575ea50cd613bb3a609555255 100644 --- a/test/integration/tools/node.js +++ b/test/integration/tools/node.js @@ -38,6 +38,7 @@ module.exports.statics = { function getTxNode(testSuite, afterBeforeHook){ let port = ++AUTO_PORT; + const now = 1481800000; var node2 = new Node({ name: "db_" + port, memory: MEMORY_MODE }, { currency: 'cc', ipv4: 'localhost', port: port, remoteipv4: 'localhost', remoteport: port, upnp: false, httplogs: false, pair: { @@ -46,7 +47,7 @@ function getTxNode(testSuite, afterBeforeHook){ }, forksize: 3, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 1, ud0: 120 }); var tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node2); @@ -62,8 +63,9 @@ function getTxNode(testSuite, afterBeforeHook){ yield toc.cert(tic); yield tic.join(); yield toc.join(); - yield node2.commitP(); - yield node2.commitP(); + yield node2.commitP({ time: now }); + yield node2.commitP({ time: now + 10 }); + yield node2.commitP({ time: now + 10 }); yield tic.sendP(51, toc); if (afterBeforeHook) { @@ -130,16 +132,16 @@ function Node (dbName, options) { * Generates next block and submit it to local node. * @returns {Function} */ - this.commit = function() { + this.commit = function(params) { return function(done) { async.waterfall([ function(next) { async.parallel({ block: function(callback){ co(function *() { - const block2 = yield that.server.BlockchainService.generateNext(); - const trial2 = yield rules.HELPERS.getTrialLevel(block2.version, that.server.keyPair.publicKey, that.server.conf, that.server.dal); - return that.server.BlockchainService.makeNextBlock(block2, trial2); + const block2 = yield that.server.BlockchainService.generateNext(params); + const trial2 = yield that.server.getBcContext().getIssuerPersonalizedDifficulty(block2.version, that.server.keyPair.publicKey); + return that.server.BlockchainService.makeNextBlock(block2, trial2, params); }) .then((block) => callback(null, block)) .catch(callback); @@ -236,6 +238,9 @@ function Node (dbName, options) { that.http.getLookup(search).then((o) => next(null, o)).catch(next); } ], function(err, res) { + if (err) { + logger.error(err); + } callback(res, done); }); }; @@ -302,5 +307,5 @@ function Node (dbName, options) { this.submitPeerP = (peer) => Q.nfcall(this.submitPeer, peer); - this.commitP = () => Q.nfcall(this.commit()); + this.commitP = (params) => Q.nfcall(this.commit(params)); } diff --git a/test/integration/tools/toolbox.js b/test/integration/tools/toolbox.js index b9003d876726bd90b0a9d47910ad28ef05121dc8..a6b606f0fb831d9dee3160ecea58e42ff80693eb 100644 --- a/test/integration/tools/toolbox.js +++ b/test/integration/tools/toolbox.js @@ -200,6 +200,7 @@ module.exports = { server.expect = (uri, expectations) => typeof expectations == 'function' ? httpTest.expectAnswer(rp(server.url(uri), { json: true }), expectations) : httpTest.expectJSON(rp(server.url(uri), { json: true }), expectations); server.expectThat = (uri, expectations) => httpTest.expectAnswer(rp(server.url(uri), { json: true }), expectations); server.expectJSON = (uri, expectations) => httpTest.expectJSON(rp(server.url(uri), { json: true }), expectations); + server.expectError = (uri, code, message) => httpTest.expectError(code, message, rp(server.url(uri), { json: true })); server.syncFrom = (otherServer, fromIncuded, toIncluded) => sync(fromIncuded, toIncluded, otherServer, server); diff --git a/test/integration/transactions-pruning.js b/test/integration/transactions-pruning.js index 3678bf982cfb4fd27688a2ba1a013c8334c62967..f70685dde3c6ad0a3fa6b3342b39a2104530cad9 100644 --- a/test/integration/transactions-pruning.js +++ b/test/integration/transactions-pruning.js @@ -7,6 +7,7 @@ const commit = require('./tools/commit'); const until = require('./tools/until'); const toolbox = require('./tools/toolbox'); const Peer = require('../../app/lib/entity/peer'); +const constants = require('../../app/lib/constants'); const s1 = toolbox.server({ currency: 'currency_one', @@ -59,9 +60,12 @@ describe("Transactions pruning", function() { })); it('double spending transaction should have been pruned', () => co(function*() { + const tmp = constants.TRANSACTION_MAX_TRIES; + constants.TRANSACTION_MAX_TRIES = 1; yield s1.commit(); yield s1.expect('/tx/history/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', (res) => { res.history.should.have.property('sending').length(0); }); + constants.TRANSACTION_MAX_TRIES = tmp; })); }); diff --git a/test/integration/transactions-test.js b/test/integration/transactions-test.js index 13efa619034145deccf114105901fc3c700fd629..89ef22e2654c5f8e85f46d06aed99194e060f087 100644 --- a/test/integration/transactions-test.js +++ b/test/integration/transactions-test.js @@ -138,7 +138,7 @@ describe("Testing transactions", function() { let tx8 = yield tic.prepareUTX(tx4, ['XHX(1872767826647264)'], [{ qty: 120, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'okk'}); // tic unlocks the XHX locked amount, and gives it to toc! yield unit.shouldFail(toc.sendTX(tx7), 'Wrong unlocker in transaction'); yield unit.shouldNotFail(toc.sendTX(tx8)); - yield s1.commit({ version: 2 }); // TX4 commited + yield s1.commit({ version: 2 }); // TX8 commited (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // That's why toc now has 1 more source... (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3); // ...and why tic's number of sources hasn't changed })); diff --git a/test/integration/v0.3-dividend.js b/test/integration/v0.3-dividend.js index dcd67ccd8bdb64b636fcdbcd073663697039ff0e..7b3cadee91b448091f9ebb25211f0a037b33d4cf 100644 --- a/test/integration/v0.3-dividend.js +++ b/test/integration/v0.3-dividend.js @@ -25,7 +25,7 @@ const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', s let now; -describe("Protocol 0.3", function() { +describe("Protocol 0.3 Dividend", function() { before(() => co(function*() { diff --git a/test/integration/v0.4-dividend.js b/test/integration/v0.4-dividend.js index 4cf7cf003c67ae6cbc668c10c24fcb34fdd83532..b9616c8e69eae139eb4ecf4d7651c8c8c4d01cbc 100644 --- a/test/integration/v0.4-dividend.js +++ b/test/integration/v0.4-dividend.js @@ -25,7 +25,7 @@ const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', s let now; -describe("Protocol 0.3", function() { +describe("Protocol 0.4 Dividend", function() { before(() => co(function*() { diff --git a/test/integration/v0.5-difficulties.js b/test/integration/v0.5-difficulties.js index 6e445d043792998c03bc9b1ee11db5d46a5dfb71..f1d059f6aa5c3c625feecb8059529057017e0f65 100644 --- a/test/integration/v0.5-difficulties.js +++ b/test/integration/v0.5-difficulties.js @@ -30,19 +30,19 @@ describe("Protocol 0.5 Difficulties", function() { s1 = res.s1; s2 = res.s2; yield [ - s1.commit({ time: now }), + s1.commit({ time: now, version: 5 }), s2.until('block', 1) ]; })); it('should be able to emit a block#1 by a different user', () => co(function*() { yield [ - s1.commit({ time: now }), // medianOfBlocksInFrame = MEDIAN([1]) = 1 + s1.commit({ time: now, version: 5 }), // medianOfBlocksInFrame = MEDIAN([1]) = 1 s2.until('block', 1), s1.until('block', 1) ]; yield [ - s2.commit({ time: now }), // medianOfBlocksInFrame = MEDIAN([1]) = 1 + s2.commit({ time: now, version: 5 }), // medianOfBlocksInFrame = MEDIAN([1]) = 1 s2.until('block', 1), s1.until('block', 1) ]; diff --git a/test/integration/wotb.js b/test/integration/wotb.js index dad0c187c415d92be762b3b0881f4178f880f49b..4aa0e4f599d126907996d4f7c4593f0ae4e246b6 100644 --- a/test/integration/wotb.js +++ b/test/integration/wotb.js @@ -28,7 +28,7 @@ const s1 = ucoin({ sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120 + sigQty: 1, dt: 1, ud0: 120 }, commonConf)); const s2 = ucoin({ @@ -41,7 +41,7 @@ const s2 = ucoin({ sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120, + sigQty: 1, dt: 1, ud0: 120, msValidity: 400 // Memberships expire after 400 second delay }, commonConf)); @@ -55,7 +55,7 @@ const s3 = ucoin({ sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7' }, participate: false, rootoffset: 10, - sigQty: 1, dt: 0, ud0: 120, + sigQty: 1, dt: 1, ud0: 120, sigValidity: 1400, sigPeriod: 0 }, commonConf)); @@ -71,7 +71,7 @@ const cat3 = user('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', const toc3 = user('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 }); const tic3 = user('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 }); -const now = Math.round(new Date().getTime() / 1000); +const now = 1482000000; const _100_PERCENT = 1.0; const MAX_DISTANCE_1 = 1; const MAX_DISTANCE_2 = 2; @@ -179,16 +179,19 @@ describe("WOTB module", function() { }); // Should make MS expire for toc2 yield commit(s2)({ - time: now + 800 + time: now + 500 }); yield commit(s2)({ - time: now + 800 + time: now + 600 }); yield cat2.join(); // Renew for not to be kicked! yield tic2.join(); // Renew for not to be kicked! yield commit(s2)({ time: now + 800 }); + yield commit(s2)({ + time: now + 800 + }); // Members excluded yield commit(s2)({ time: now + 800 @@ -254,7 +257,7 @@ describe("WOTB module", function() { it('two first commits: the WoT is new and OK', function() { return co(function *() { - yield commit(s3)(); + yield commit(s3)({ time: now }); yield commit(s3)({ time: now + 1200 }); @@ -346,7 +349,7 @@ describe("WOTB module", function() { yield tic3.cert(cat3); yield cat3.join(); yield commit(s3)({ - time: now + 2700 + time: now + 5000 }); /** * cat <-- tic <-- [toc]