diff --git a/.eslintignore b/.eslintignore index e2dd35aa5e71968b1bcc5418846b93513bc7a119..f208f45df1239a957a319274cb070ed7c3f1e7fd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,9 @@ app/lib/blockchain/*.js app/lib/blockchain/interfaces/*.js +app/lib/computation/*.js +app/lib/db/*.js +app/lib/dto/*.js +app/lib/indexer.js +app/lib/common.js test/blockchain/*.js test/blockchain/lib/*.js \ No newline at end of file diff --git a/.gitignore b/.gitignore index d51ee51c1d0bb6d96722cd9ccee5615b1245726a..304462b9e97a24e302a04c323e942e4111fbfa4b 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,10 @@ test/blockchain/lib/*.js.map app/lib/blockchain/*.js app/lib/blockchain/*.js.map app/lib/blockchain/interfaces/*.js -app/lib/blockchain/interfaces/*.js.map \ No newline at end of file +app/lib/blockchain/interfaces/*.js.map +app/lib/computation/*.js +app/lib/computation/*.js.map +app/lib/common.js* +app/lib/db/*.js* +app/lib/dto/*.js* +app/lib/indexer.js* \ No newline at end of file diff --git a/app/lib/blockchain/BasicBlockchain.ts b/app/lib/blockchain/BasicBlockchain.ts index d251b4111b3b0ed1b5a5f79f05e6b5f40e5fad68..dbf9fbabdc496accefed78583990709b6c529baf 100644 --- a/app/lib/blockchain/BasicBlockchain.ts +++ b/app/lib/blockchain/BasicBlockchain.ts @@ -9,7 +9,7 @@ export class BasicBlockchain { /** * Adds a block at the end of the blockchain. */ - pushBlock(b) { + pushBlock(b:any) { return this.op.store(b) } @@ -18,7 +18,7 @@ export class BasicBlockchain { * @param number block ID. * @returns {*} Promise<Block> */ - getBlock(number) { + getBlock(number:number) { return this.op.read(number) } @@ -44,7 +44,7 @@ export class BasicBlockchain { * @param n Quantity from top. E.g. `1` = [HEAD], `3` = [HEAD, HEAD~1, HEAD~2], etc. * @returns {*} Promise<Block> */ - headRange(n) { + headRange(n:number) { return this.op.headRange(n) } diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts index ea6a9a46523c00531b8f2316b22d7f842aa345ae..2de2ea33edc3a84effe3fd814eb08ae661001661 100644 --- a/app/lib/blockchain/DuniterBlockchain.ts +++ b/app/lib/blockchain/DuniterBlockchain.ts @@ -1,9 +1,15 @@ import {MiscIndexedBlockchain} from "./MiscIndexedBlockchain" +import {IindexEntry, IndexEntry, Indexer, MindexEntry, SindexEntry} from "../indexer" +import {BlockchainOperator} from "./interfaces/BlockchainOperator" +import {ConfDTO} from "../dto/ConfDTO" +import {BlockDTO} from "../dto/BlockDTO" +import {DBHead} from "../db/DBHead" +import {IdentityDTO} from "../dto/IdentityDTO" +import {DBBlock} from "../db/DBBlock" const _ = require('underscore') const Q = require('q') const rules = require('../rules') -const indexer = require('../indexer') const common = require('duniter-common') const Block = require('../entity/block') const Identity = require('../entity/identity') @@ -11,137 +17,123 @@ const Certification = require('../entity/certification') const Membership = require('../entity/membership') const Transaction = require('../entity/transaction') -const statTests = { - 'newcomers': 'identities', - 'certs': 'certifications', - 'joiners': 'joiners', - 'actives': 'actives', - 'leavers': 'leavers', - 'revoked': 'revoked', - 'excluded': 'excluded', - 'ud': 'dividend', - 'tx': 'transactions' -}; - -const statNames = ['newcomers', 'certs', 'joiners', 'actives', 'leavers', 'revoked', 'excluded', 'ud', 'tx']; - export class DuniterBlockchain extends MiscIndexedBlockchain { - constructor(blockchainStorage, dal) { + constructor(blockchainStorage:BlockchainOperator, dal:any) { super(blockchainStorage, dal.mindexDAL, dal.iindexDAL, dal.sindexDAL, dal.cindexDAL) } - async checkBlock(block, withPoWAndSignature, conf, dal) { - const index = indexer.localIndex(block, conf) + async checkBlock(block:BlockDTO, withPoWAndSignature:boolean, conf: ConfDTO, dal:any) { + const index = Indexer.localIndex(block, conf) if (withPoWAndSignature) { await rules.CHECK.ASYNC.ALL_LOCAL(block, conf, index) } else { await rules.CHECK.ASYNC.ALL_LOCAL_BUT_POW(block, conf, index) } - const HEAD = await indexer.completeGlobalScope(block, conf, index, dal); + const HEAD = await Indexer.completeGlobalScope(block, conf, index, dal); const HEAD_1 = await dal.bindexDAL.head(1); - const mindex = indexer.mindex(index); - const iindex = indexer.iindex(index); - const sindex = indexer.sindex(index); - const cindex = indexer.cindex(index); + const mindex = Indexer.mindex(index); + const iindex = Indexer.iindex(index); + const sindex = Indexer.sindex(index); + const cindex = Indexer.cindex(index); // BR_G49 - if (indexer.ruleVersion(HEAD, HEAD_1) === false) throw Error('ruleVersion'); + if (Indexer.ruleVersion(HEAD, HEAD_1) === false) throw Error('ruleVersion'); // BR_G50 - if (indexer.ruleBlockSize(HEAD) === false) throw Error('ruleBlockSize'); + if (Indexer.ruleBlockSize(HEAD) === false) throw Error('ruleBlockSize'); // BR_G98 - if (indexer.ruleCurrency(block, HEAD) === false) throw Error('ruleCurrency'); + if (Indexer.ruleCurrency(block, HEAD) === false) throw Error('ruleCurrency'); // BR_G51 - if (indexer.ruleNumber(block, HEAD) === false) throw Error('ruleNumber'); + if (Indexer.ruleNumber(block, HEAD) === false) throw Error('ruleNumber'); // BR_G52 - if (indexer.rulePreviousHash(block, HEAD) === false) throw Error('rulePreviousHash'); + if (Indexer.rulePreviousHash(block, HEAD) === false) throw Error('rulePreviousHash'); // BR_G53 - if (indexer.rulePreviousIssuer(block, HEAD) === false) throw Error('rulePreviousIssuer'); + if (Indexer.rulePreviousIssuer(block, HEAD) === false) throw Error('rulePreviousIssuer'); // BR_G101 - if (indexer.ruleIssuerIsMember(HEAD) === false) throw Error('ruleIssuerIsMember'); + if (Indexer.ruleIssuerIsMember(HEAD) === false) throw Error('ruleIssuerIsMember'); // BR_G54 - if (indexer.ruleIssuersCount(block, HEAD) === false) throw Error('ruleIssuersCount'); + if (Indexer.ruleIssuersCount(block, HEAD) === false) throw Error('ruleIssuersCount'); // BR_G55 - if (indexer.ruleIssuersFrame(block, HEAD) === false) throw Error('ruleIssuersFrame'); + if (Indexer.ruleIssuersFrame(block, HEAD) === false) throw Error('ruleIssuersFrame'); // BR_G56 - if (indexer.ruleIssuersFrameVar(block, HEAD) === false) throw Error('ruleIssuersFrameVar'); + if (Indexer.ruleIssuersFrameVar(block, HEAD) === false) throw Error('ruleIssuersFrameVar'); // BR_G57 - if (indexer.ruleMedianTime(block, HEAD) === false) throw Error('ruleMedianTime'); + if (Indexer.ruleMedianTime(block, HEAD) === false) throw Error('ruleMedianTime'); // BR_G58 - if (indexer.ruleDividend(block, HEAD) === false) throw Error('ruleDividend'); + if (Indexer.ruleDividend(block, HEAD) === false) throw Error('ruleDividend'); // BR_G59 - if (indexer.ruleUnitBase(block, HEAD) === false) throw Error('ruleUnitBase'); + if (Indexer.ruleUnitBase(block, HEAD) === false) throw Error('ruleUnitBase'); // BR_G60 - if (indexer.ruleMembersCount(block, HEAD) === false) throw Error('ruleMembersCount'); + if (Indexer.ruleMembersCount(block, HEAD) === false) throw Error('ruleMembersCount'); // BR_G61 - if (indexer.rulePowMin(block, HEAD) === false) throw Error('rulePowMin'); + if (Indexer.rulePowMin(block, HEAD) === false) throw Error('rulePowMin'); if (withPoWAndSignature) { // BR_G62 - if (indexer.ruleProofOfWork(HEAD) === false) throw Error('ruleProofOfWork'); + if (Indexer.ruleProofOfWork(HEAD) === false) throw Error('ruleProofOfWork'); } // BR_G63 - if (indexer.ruleIdentityWritability(iindex, conf) === false) throw Error('ruleIdentityWritability'); + if (Indexer.ruleIdentityWritability(iindex, conf) === false) throw Error('ruleIdentityWritability'); // BR_G64 - if (indexer.ruleMembershipWritability(mindex, conf) === false) throw Error('ruleMembershipWritability'); + if (Indexer.ruleMembershipWritability(mindex, conf) === false) throw Error('ruleMembershipWritability'); // BR_G108 - if (indexer.ruleMembershipPeriod(mindex) === false) throw Error('ruleMembershipPeriod'); + if (Indexer.ruleMembershipPeriod(mindex) === false) throw Error('ruleMembershipPeriod'); // BR_G65 - if (indexer.ruleCertificationWritability(cindex, conf) === false) throw Error('ruleCertificationWritability'); + if (Indexer.ruleCertificationWritability(cindex, conf) === false) throw Error('ruleCertificationWritability'); // BR_G66 - if (indexer.ruleCertificationStock(cindex, conf) === false) throw Error('ruleCertificationStock'); + if (Indexer.ruleCertificationStock(cindex, conf) === false) throw Error('ruleCertificationStock'); // BR_G67 - if (indexer.ruleCertificationPeriod(cindex) === false) throw Error('ruleCertificationPeriod'); + if (Indexer.ruleCertificationPeriod(cindex) === false) throw Error('ruleCertificationPeriod'); // BR_G68 - if (indexer.ruleCertificationFromMember(HEAD, cindex) === false) throw Error('ruleCertificationFromMember'); + if (Indexer.ruleCertificationFromMember(HEAD, cindex) === false) throw Error('ruleCertificationFromMember'); // BR_G69 - if (indexer.ruleCertificationToMemberOrNewcomer(cindex) === false) throw Error('ruleCertificationToMemberOrNewcomer'); + if (Indexer.ruleCertificationToMemberOrNewcomer(cindex) === false) throw Error('ruleCertificationToMemberOrNewcomer'); // BR_G70 - if (indexer.ruleCertificationToLeaver(cindex) === false) throw Error('ruleCertificationToLeaver'); + if (Indexer.ruleCertificationToLeaver(cindex) === false) throw Error('ruleCertificationToLeaver'); // BR_G71 - if (indexer.ruleCertificationReplay(cindex) === false) throw Error('ruleCertificationReplay'); + if (Indexer.ruleCertificationReplay(cindex) === false) throw Error('ruleCertificationReplay'); // BR_G72 - if (indexer.ruleCertificationSignature(cindex) === false) throw Error('ruleCertificationSignature'); + if (Indexer.ruleCertificationSignature(cindex) === false) throw Error('ruleCertificationSignature'); // BR_G73 - if (indexer.ruleIdentityUIDUnicity(iindex) === false) throw Error('ruleIdentityUIDUnicity'); + if (Indexer.ruleIdentityUIDUnicity(iindex) === false) throw Error('ruleIdentityUIDUnicity'); // BR_G74 - if (indexer.ruleIdentityPubkeyUnicity(iindex) === false) throw Error('ruleIdentityPubkeyUnicity'); + if (Indexer.ruleIdentityPubkeyUnicity(iindex) === false) throw Error('ruleIdentityPubkeyUnicity'); // BR_G75 - if (indexer.ruleMembershipSuccession(mindex) === false) throw Error('ruleMembershipSuccession'); + if (Indexer.ruleMembershipSuccession(mindex) === false) throw Error('ruleMembershipSuccession'); // BR_G76 - if (indexer.ruleMembershipDistance(HEAD, mindex) === false) throw Error('ruleMembershipDistance'); + if (Indexer.ruleMembershipDistance(HEAD, mindex) === false) throw Error('ruleMembershipDistance'); // BR_G77 - if (indexer.ruleMembershipOnRevoked(mindex) === false) throw Error('ruleMembershipOnRevoked'); + if (Indexer.ruleMembershipOnRevoked(mindex) === false) throw Error('ruleMembershipOnRevoked'); // BR_G78 - if (indexer.ruleMembershipJoinsTwice(mindex) === false) throw Error('ruleMembershipJoinsTwice'); + if (Indexer.ruleMembershipJoinsTwice(mindex) === false) throw Error('ruleMembershipJoinsTwice'); // BR_G79 - if (indexer.ruleMembershipEnoughCerts(mindex) === false) throw Error('ruleMembershipEnoughCerts'); + if (Indexer.ruleMembershipEnoughCerts(mindex) === false) throw Error('ruleMembershipEnoughCerts'); // BR_G80 - if (indexer.ruleMembershipLeaverIsMember(mindex) === false) throw Error('ruleMembershipLeaverIsMember'); + if (Indexer.ruleMembershipLeaverIsMember(mindex) === false) throw Error('ruleMembershipLeaverIsMember'); // BR_G81 - if (indexer.ruleMembershipActiveIsMember(mindex) === false) throw Error('ruleMembershipActiveIsMember'); + if (Indexer.ruleMembershipActiveIsMember(mindex) === false) throw Error('ruleMembershipActiveIsMember'); // BR_G82 - if (indexer.ruleMembershipRevokedIsMember(mindex) === false) throw Error('ruleMembershipRevokedIsMember'); + if (Indexer.ruleMembershipRevokedIsMember(mindex) === false) throw Error('ruleMembershipRevokedIsMember'); // BR_G83 - if (indexer.ruleMembershipRevokedSingleton(mindex) === false) throw Error('ruleMembershipRevokedSingleton'); + if (Indexer.ruleMembershipRevokedSingleton(mindex) === false) throw Error('ruleMembershipRevokedSingleton'); // BR_G84 - if (indexer.ruleMembershipRevocationSignature(mindex) === false) throw Error('ruleMembershipRevocationSignature'); + if (Indexer.ruleMembershipRevocationSignature(mindex) === false) throw Error('ruleMembershipRevocationSignature'); // BR_G85 - if (indexer.ruleMembershipExcludedIsMember(iindex) === false) throw Error('ruleMembershipExcludedIsMember'); + if (Indexer.ruleMembershipExcludedIsMember(iindex) === false) throw Error('ruleMembershipExcludedIsMember'); // BR_G86 - if ((await indexer.ruleToBeKickedArePresent(iindex, dal)) === false) throw Error('ruleToBeKickedArePresent'); + if ((await Indexer.ruleToBeKickedArePresent(iindex, dal)) === false) throw Error('ruleToBeKickedArePresent'); // BR_G103 - if (indexer.ruleTxWritability(sindex) === false) throw Error('ruleTxWritability'); + if (Indexer.ruleTxWritability(sindex) === false) throw Error('ruleTxWritability'); // BR_G87 - if (indexer.ruleInputIsAvailable(sindex) === false) throw Error('ruleInputIsAvailable'); + if (Indexer.ruleInputIsAvailable(sindex) === false) throw Error('ruleInputIsAvailable'); // BR_G88 - if (indexer.ruleInputIsUnlocked(sindex) === false) throw Error('ruleInputIsUnlocked'); + if (Indexer.ruleInputIsUnlocked(sindex) === false) throw Error('ruleInputIsUnlocked'); // BR_G89 - if (indexer.ruleInputIsTimeUnlocked(sindex) === false) throw Error('ruleInputIsTimeUnlocked'); + if (Indexer.ruleInputIsTimeUnlocked(sindex) === false) throw Error('ruleInputIsTimeUnlocked'); // BR_G90 - if (indexer.ruleOutputBase(sindex, HEAD_1) === false) throw Error('ruleOutputBase'); + if (Indexer.ruleOutputBase(sindex, HEAD_1) === false) throw Error('ruleOutputBase'); // Check document's coherence - const matchesList = (regexp, list) => { + const matchesList = (regexp:RegExp, list:string[]) => { let i = 0; let found = ""; while (!found && i < list.length) { @@ -169,7 +161,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { return { index, HEAD } } - async pushTheBlock(obj, index, HEAD, conf, dal, logger) { + async pushTheBlock(obj:BlockDTO, index:IndexEntry[], HEAD:DBHead, conf:ConfDTO, dal:any, logger:any) { const start = Date.now(); const block = new Block(obj); try { @@ -196,7 +188,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { // await supra.recordIndex(index) } - async saveBlockData(current, block, conf, dal, logger, index, HEAD) { + async saveBlockData(current:DBBlock, block:BlockDTO, conf:ConfDTO, dal:any, logger:any, index:IndexEntry[], HEAD:DBHead) { if (block.number == 0) { await this.saveParametersForRoot(block, conf, dal); } @@ -234,9 +226,10 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { await dal.trimIndexes(indexes.HEAD.number - MAX_BINDEX_SIZE); } - await this.updateBlocksComputedVars(current, block); + const dbb = DBBlock.fromBlockDTO(block) + await this.updateBlocksComputedVars(current, dbb); // Saves the block (DAL) - await dal.saveBlock(block); + await dal.saveBlock(dbb); // --> Update links await dal.updateWotbLinks(indexes.cindex); @@ -255,7 +248,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { return block; } - async saveParametersForRoot(block, conf, dal) { + async saveParametersForRoot(block:BlockDTO, conf:ConfDTO, dal:any) { if (block.parameters) { const bconf = Block.statics.getConf(block); conf.c = bconf.c; @@ -285,27 +278,20 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - async createNewcomers(iindex, dal, logger) { + async createNewcomers(iindex:IindexEntry[], dal:any, logger:any) { for (const entry of iindex) { if (entry.op == common.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. - await this.cleanRejectedIdentities({ - pubkey: entry.pub, - uid: entry.uid - }, dal); + await dal.removeUnWrittenWithPubkey(entry.pub) + await dal.removeUnWrittenWithUID(entry.uid) } } } - async cleanRejectedIdentities(idty, dal) { - await dal.removeUnWrittenWithPubkey(idty.pubkey); - await dal.removeUnWrittenWithUID(idty.uid); - } - - async updateMembers(block, dal) { + async updateMembers(block:BlockDTO, dal:any) { // Joiners (come back) for (const inlineMS of block.joiners) { let ms = Membership.statics.fromInline(inlineMS); @@ -324,13 +310,13 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - async updateWallets(sindex, aDal, reverse = false) { + async updateWallets(sindex:SindexEntry[], aDal:any, reverse = false) { const differentConditions = _.uniq(sindex.map((entry) => entry.conditions)) for (const conditions of differentConditions) { - const creates = _.filter(sindex, (entry) => entry.conditions === conditions && entry.op === common.constants.IDX_CREATE) - const updates = _.filter(sindex, (entry) => entry.conditions === conditions && entry.op === common.constants.IDX_UPDATE) - const positives = creates.reduce((sum, src) => sum + src.amount * Math.pow(10, src.base), 0) - const negatives = updates.reduce((sum, src) => sum + src.amount * Math.pow(10, src.base), 0) + const creates = _.filter(sindex, (entry:SindexEntry) => entry.conditions === conditions && entry.op === common.constants.IDX_CREATE) + const updates = _.filter(sindex, (entry:SindexEntry) => entry.conditions === conditions && entry.op === common.constants.IDX_UPDATE) + const positives = creates.reduce((sum:number, src:SindexEntry) => sum + src.amount * Math.pow(10, src.base), 0) + const negatives = updates.reduce((sum:number, src:SindexEntry) => sum + src.amount * Math.pow(10, src.base), 0) const wallet = await aDal.getWallet(conditions) let variation = positives - negatives if (reverse) { @@ -342,7 +328,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - async revertBlock(number, hash, dal) { + async revertBlock(number:number, hash:string, dal:any) { const blockstamp = [number, hash].join('-'); @@ -386,7 +372,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { await this.undoDeleteTransactions(block, dal) } - async undoMembersUpdate(blockstamp, dal) { + async undoMembersUpdate(blockstamp:string, dal:any) { const joiners = await dal.iindexDAL.getWrittenOn(blockstamp); for (const entry of joiners) { // Undo 'join' which can be either newcomers or comebackers @@ -416,7 +402,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - async undoDeleteTransactions(block, dal) { + async undoDeleteTransactions(block:BlockDTO, dal:any) { for (const obj of block.transactions) { obj.currency = block.currency; let tx = new Transaction(obj); @@ -430,7 +416,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { * @param block Block in which are contained the certifications to remove from sandbox. * @param dal The DAL */ - async removeCertificationsFromSandbox(block, dal) { + async removeCertificationsFromSandbox(block:BlockDTO, dal:any) { for (let inlineCert of block.certifications) { let cert = Certification.statics.fromInline(inlineCert); let idty = await dal.getWritten(cert.to); @@ -445,7 +431,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { * @param block Block in which are contained the certifications to remove from sandbox. * @param dal The DAL */ - async removeMembershipsFromSandbox(block, dal) { + async removeMembershipsFromSandbox(block:BlockDTO, dal:any) { const mss = block.joiners.concat(block.actives).concat(block.leavers); for (const inlineMS of mss) { let ms = Membership.statics.fromInline(inlineMS); @@ -453,14 +439,14 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - async computeToBeRevoked(mindex, dal) { - const revocations = _.filter(mindex, (entry) => entry.revoked_on); + async computeToBeRevoked(mindex:MindexEntry[], dal:any) { + const revocations = _.filter(mindex, (entry:MindexEntry) => entry.revoked_on); for (const revoked of revocations) { await dal.setRevoked(revoked.pub, true); } } - async deleteTransactions(block, dal) { + async deleteTransactions(block:BlockDTO, dal:any) { for (const obj of block.transactions) { obj.currency = block.currency; const tx = new Transaction(obj); @@ -469,7 +455,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { } } - updateBlocksComputedVars(current, block): Promise<void> { + updateBlocksComputedVars(current:DBBlock, block:DBBlock): Promise<void> { // Unit Base block.unitbase = (block.dividend && block.unitbase) || (current && current.unitbase) || 0; // Monetary Mass update @@ -487,19 +473,28 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { return Promise.resolve() } - static pushStatsForBlocks(blocks, dal) { - const stats = {}; + static pushStatsForBlocks(blocks:BlockDTO[], dal:any) { + const stats: { [k:string]: { blocks: number[], lastParsedBlock:number }} = {}; // Stats for (const block of blocks) { - for (const statName of statNames) { - if (!stats[statName]) { - stats[statName] = { blocks: [] }; + const values = [ + { name: 'newcomers', value: block.identities }, + { name: 'certs', value: block.certifications }, + { name: 'joiners', value: block.joiners }, + { name: 'actives', value: block.actives }, + { name: 'leavers', value: block.leavers }, + { name: 'revoked', value: block.revoked }, + { name: 'excluded', value: block.excluded }, + { name: 'ud', value: block.dividend }, + { name: 'tx', value: block.transactions } + ] + for (const element of values) { + if (!stats[element.name]) { + stats[element.name] = { blocks: [], lastParsedBlock: -1 }; } - const stat = stats[statName]; - const testProperty = statTests[statName]; - const value = block[testProperty]; - const isPositiveValue = value && typeof value != 'object'; - const isNonEmptyArray = value && typeof value == 'object' && value.length > 0; + const stat = stats[element.name] + const isPositiveValue = element.value && typeof element.value != 'object'; + const isNonEmptyArray = element.value && typeof element.value == 'object' && element.value.length > 0; if (isPositiveValue || isNonEmptyArray) { stat.blocks.push(block.number); } @@ -509,7 +504,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain { return dal.pushStats(stats); } - async pushSideBlock(obj, dal, logger) { + async pushSideBlock(obj:BlockDTO, dal:any, logger:any) { const start = Date.now(); const block = new Block(obj); block.fork = true; diff --git a/app/lib/blockchain/IndexedBlockchain.ts b/app/lib/blockchain/IndexedBlockchain.ts index 67fa5a0913f772019e25b69bc419f7ca654d7ff9..2545c3b660f3dd26be87782d456536580a94b89d 100644 --- a/app/lib/blockchain/IndexedBlockchain.ts +++ b/app/lib/blockchain/IndexedBlockchain.ts @@ -2,32 +2,33 @@ import {BasicBlockchain} from "./BasicBlockchain" import {IndexOperator} from "./interfaces/IndexOperator" import {BlockchainOperator} from "./interfaces/BlockchainOperator" -import * as _ from "underscore" + +const _ = require('underscore') export class IndexedBlockchain extends BasicBlockchain { private initIndexer: Promise<void> - constructor(bcOperations: BlockchainOperator, private indexOperations: IndexOperator, private numberField, private pkFields: any) { + constructor(bcOperations: BlockchainOperator, private indexOperations: IndexOperator, private numberField: string, private pkFields: any) { super(bcOperations) this.initIndexer = indexOperations.initIndexer(pkFields) } - async recordIndex(index) { + async recordIndex(index: { [index: string]: any }) { // Wait indexer init await this.initIndexer return this.indexOperations.recordIndex(index) } - async indexTrim(maxNumber) { + async indexTrim(maxNumber:number) { // Wait indexer init await this.initIndexer const subIndexes = await this.indexOperations.getSubIndexes() // Trim the subIndexes - const records = {} + const records: { [index: string]: any } = {} for (const subIndex of subIndexes) { records[subIndex] = [] const pks = typeof this.pkFields[subIndex].pk !== 'string' && this.pkFields[subIndex].pk.length ? Array.from(this.pkFields[subIndex].pk) : [this.pkFields[subIndex].pk] @@ -57,7 +58,7 @@ export class IndexedBlockchain extends BasicBlockchain { return Promise.resolve() } - async indexCount(indexName, criterias) { + async indexCount(indexName: string, criterias: { [index: string]: any }) { // Wait indexer init await this.initIndexer @@ -66,7 +67,7 @@ export class IndexedBlockchain extends BasicBlockchain { return records.length } - async indexReduce(indexName, criterias) { + async indexReduce(indexName: string, criterias: { [index: string]: any }) { // Wait indexer init await this.initIndexer @@ -75,7 +76,7 @@ export class IndexedBlockchain extends BasicBlockchain { return reduce(records) } - async indexReduceGroupBy(indexName, criterias, properties) { + async indexReduceGroupBy(indexName: string, criterias: { [index: string]: any }, properties: string[]) { // Wait indexer init await this.initIndexer @@ -84,17 +85,17 @@ export class IndexedBlockchain extends BasicBlockchain { return reduceBy(records, properties) } - async indexRevert(blockNumber) { + async indexRevert(blockNumber:number) { const subIndexes = await this.indexOperations.getSubIndexes() for (const subIndex of subIndexes) { - const removeCriterias = {} + const removeCriterias: { [index: string]: any } = {} removeCriterias[this.numberField] = blockNumber await this.indexOperations.removeWhere(subIndex, removeCriterias) } } } -function reduce(records) { +function reduce(records: any[]) { return records.reduce((obj, record) => { const keys = Object.keys(record); for (const k of keys) { @@ -106,18 +107,18 @@ function reduce(records) { }, {}); } -function reduceBy(reducables, properties) { +function reduceBy(reducables: any[], properties: string[]) { 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 _.values(reduced).map((value) => reduce(value)) + return _.values(reduced).map((rows: any[]) => reduce(rows)) } -function criteriasFromPks(pks, values) { - const criterias = {} +function criteriasFromPks(pks: string[], values: any): { [index: string]: any } { + const criterias: { [index: string]: any } = {} for (const key of pks) { criterias[key] = values[key] } diff --git a/app/lib/blockchain/MiscIndexedBlockchain.ts b/app/lib/blockchain/MiscIndexedBlockchain.ts index 05b7acdb99d027e34ebbd79390c4fc72bdb16197..dbde55274aa6eca25fe8b7ea328c5823fdcc8141 100644 --- a/app/lib/blockchain/MiscIndexedBlockchain.ts +++ b/app/lib/blockchain/MiscIndexedBlockchain.ts @@ -1,20 +1,21 @@ "use strict" import {IndexedBlockchain} from "./IndexedBlockchain" import {SQLIndex} from "./SqlIndex" +import {BlockchainOperator} from "./interfaces/BlockchainOperator" export class MiscIndexedBlockchain extends IndexedBlockchain { - constructor(blockchainStorage, mindexDAL, iindexDAL, sindexDAL, cindexDAL) { + constructor(blockchainStorage: BlockchainOperator, mindexDAL:any, iindexDAL:any, sindexDAL:any, cindexDAL:any) { super(blockchainStorage, new SQLIndex(null, { m_index: { handler: mindexDAL }, i_index: { handler: iindexDAL }, s_index: { handler: sindexDAL, - findTrimable: (maxNumber) => sindexDAL.query('SELECT * FROM s_index WHERE consumed AND writtenOn < ?', [maxNumber]) + findTrimable: (maxNumber:number) => sindexDAL.query('SELECT * FROM s_index WHERE consumed AND writtenOn < ?', [maxNumber]) }, c_index: { handler: cindexDAL, - findTrimable: (maxNumber) => cindexDAL.query('SELECT * FROM c_index WHERE expired_on > 0 AND writtenOn < ?', [maxNumber]) + findTrimable: (maxNumber:number) => cindexDAL.query('SELECT * FROM c_index WHERE expired_on > 0 AND writtenOn < ?', [maxNumber]) } }), 'writtenOn', { m_index: { diff --git a/app/lib/blockchain/SqlBlockchain.ts b/app/lib/blockchain/SqlBlockchain.ts index b2037c306a705dbc1faf3eecd5f484c94651f42c..7b474199b11c68814f60de435197e7158cdb79d6 100644 --- a/app/lib/blockchain/SqlBlockchain.ts +++ b/app/lib/blockchain/SqlBlockchain.ts @@ -1,7 +1,7 @@ "use strict" import {BlockchainOperator} from "./interfaces/BlockchainOperator" -const indexer = require('../../lib/indexer') +const indexer = require('../../lib/indexer').Indexer export class SQLBlockchain implements BlockchainOperator { diff --git a/app/lib/blockchain/interfaces/BlockchainOperator.ts b/app/lib/blockchain/interfaces/BlockchainOperator.ts index d1a61b3ff50745ae33be6517307faeede2dedd8c..12e96f11a481dfbf8fd2a3361d74ebca52c21486 100644 --- a/app/lib/blockchain/interfaces/BlockchainOperator.ts +++ b/app/lib/blockchain/interfaces/BlockchainOperator.ts @@ -6,7 +6,7 @@ export interface BlockchainOperator { * Pushes a new block at the top of the blockchain. * @param b Block. */ - store(b):Promise<any> + store(b:any):Promise<any> /** * Reads the block at index `i`. diff --git a/app/lib/common.ts b/app/lib/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..a16cf1bdc81a704b370b565dd27e86b397cbd8e6 --- /dev/null +++ b/app/lib/common.ts @@ -0,0 +1,5 @@ +const common = require('duniter-common') + +export function hashf(str:string) { + return common.hashf(str).toUpperCase() +} diff --git a/app/lib/computation/BlockchainContext.ts b/app/lib/computation/BlockchainContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..5dc02dd817c747a1aac199da72efe7b059d5b262 --- /dev/null +++ b/app/lib/computation/BlockchainContext.ts @@ -0,0 +1,159 @@ +"use strict"; +import {BlockDTO} from "../dto/BlockDTO" +import {DuniterBlockchain} from "../blockchain/DuniterBlockchain" +import {QuickSynchronizer} from "./QuickSync" +import {DBHead} from "../db/DBHead" +const _ = require('underscore'); +const indexer = require('../indexer').Indexer +const constants = require('../constants'); +const Block = require('../entity/block'); + +export class BlockchainContext { + + private conf:any + private dal:any + private logger:any + private blockchain:DuniterBlockchain + private quickSynchronizer:QuickSynchronizer + + /** + * 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. + */ + private vHEAD:any + + /** + * The currently written HEAD, aka. HEAD_1 relatively to incoming HEAD. + */ + private vHEAD_1:any + + private HEADrefreshed: Promise<any> | null = Promise.resolve(); + + /** + * 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. + */ + private refreshHead(): Promise<void> { + this.HEADrefreshed = (async (): Promise<void> => { + this.vHEAD_1 = await this.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 (!this.vHEAD_1) { + block = { + version: constants.BLOCK_GENERATED_VERSION, + time: Math.round(Date.now() / 1000), + powMin: this.conf.powMin || 0, + powZeros: 0, + powRemainder: 0, + avgBlockSize: 0 + }; + } else { + block = { version: this.vHEAD_1.version }; + } + this.vHEAD = await indexer.completeGlobalScope(Block.statics.fromJSON(block), this.conf, [], this.dal); + })() + return this.HEADrefreshed; + } + + /** + * Gets a copy of vHEAD, extended with some extra properties. + * @param props The extra properties to add. + */ + async getvHeadCopy(props: any): Promise<any> { + if (!this.vHEAD) { + await this.refreshHead(); + } + const copy: any = {}; + const keys = Object.keys(this.vHEAD); + for (const k of keys) { + copy[k] = this.vHEAD[k]; + } + _.extend(copy, props); + return copy; + } + + /** + * Get currently written HEAD. + */ + async getvHEAD_1(): Promise<any> { + if (!this.vHEAD) { + await this.refreshHead(); + } + return this.vHEAD_1 + } + + /** + * Utility method: gives the personalized difficulty level of a given issuer for next block. + * @param issuer The issuer we want to get the difficulty level. + */ + async getIssuerPersonalizedDifficulty(issuer: string): Promise<any> { + const local_vHEAD = await this.getvHeadCopy({ issuer }); + await indexer.preparePersonalizedPoW(local_vHEAD, this.vHEAD_1, this.dal.range, this.conf) + return local_vHEAD.issuerDiff; + } + + setConfDAL(newConf: any, newDAL: any, theBlockchain: DuniterBlockchain, theQuickSynchronizer: QuickSynchronizer): void { + this.dal = newDAL; + this.conf = newConf; + this.blockchain = theBlockchain + this.quickSynchronizer = theQuickSynchronizer + this.logger = require('../logger')(this.dal.profile); + } + + checkBlock(block: BlockDTO, withPoWAndSignature = true): Promise<any> { + return this.blockchain.checkBlock(block, withPoWAndSignature, this.conf, this.dal) + } + + async addBlock(obj: BlockDTO, index: any, HEAD: DBHead): Promise<any> { + const block = await this.blockchain.pushTheBlock(obj, index, HEAD, this.conf, this.dal, this.logger) + this.vHEAD_1 = this.vHEAD = this.HEADrefreshed = null + return block + } + + addSideBlock(obj:BlockDTO): Promise<any> { + return this.blockchain.pushSideBlock(obj, this.dal, this.logger) + } + + async revertCurrentBlock(): Promise<any> { + const head_1 = await this.dal.bindexDAL.head(1); + this.logger.debug('Reverting block #%s...', head_1.number); + const res = await this.blockchain.revertBlock(head_1.number, head_1.hash, this.dal) + this.logger.debug('Reverted block #%s', head_1.number); + // Invalidates the head, since it has changed. + await this.refreshHead(); + return res; + } + + async applyNextAvailableFork(): Promise<any> { + const current = await this.current(); + this.logger.debug('Find next potential block #%s...', current.number + 1); + const forks = await this.dal.getForkBlocksFollowing(current); + if (!forks.length) { + throw constants.ERRORS.NO_POTENTIAL_FORK_AS_NEXT; + } + const block = forks[0]; + const { index, HEAD } = await this.checkBlock(block, constants.WITH_SIGNATURES_AND_POW); + await this.addBlock(block, index, HEAD); + this.logger.debug('Applied block #%s', block.number); + } + + current(): Promise<any> { + return this.dal.getCurrentBlockOrNull() + } + + async checkHaveEnoughLinks(target: string, newLinks: any): Promise<any> { + const links = await this.dal.getValidLinksTo(target); + let count = links.length; + if (newLinks[target] && newLinks[target].length) { + count += newLinks[target].length; + } + if (count < this.conf.sigQty) { + throw 'Key ' + target + ' does not have enough links (' + count + '/' + this.conf.sigQty + ')'; + } + } + + quickApplyBlocks(blocks:BlockDTO[], to: number | null): Promise<any> { + return this.quickSynchronizer.quickApplyBlocks(blocks, to) + } +} diff --git a/app/lib/computation/QuickSync.ts b/app/lib/computation/QuickSync.ts new file mode 100644 index 0000000000000000000000000000000000000000..261878e4f824b79edae77bc236e0183cee7ab9d1 --- /dev/null +++ b/app/lib/computation/QuickSync.ts @@ -0,0 +1,264 @@ +"use strict" +import {DuniterBlockchain} from "../blockchain/DuniterBlockchain" +import {BlockDTO} from "../dto/BlockDTO" +import {DBTransaction} from "../db/DBTransaction" +import {Indexer} from "../indexer" +import {ConfDTO} from "../dto/ConfDTO" + +const Q = require('q'); +const _ = require('underscore') +const constants = require('../constants') +const Block = require('../entity/block') + +let sync_bindex: any [] = []; +let sync_iindex: any[] = []; +let sync_mindex: any[] = []; +let sync_cindex: any[] = []; +let sync_sindex: any[] = []; +let sync_bindexSize = 0; +let sync_allBlocks: BlockDTO[] = []; +let sync_expires: number[] = []; +let sync_nextExpiring = 0; +let sync_currConf: ConfDTO; +const sync_memoryWallets: any = {} +const sync_memoryDAL = { + getWallet: (conditions: string) => Promise.resolve(sync_memoryWallets[conditions] || { conditions, balance: 0 }), + saveWallet: async (wallet: any) => { + // Make a copy + sync_memoryWallets[wallet.conditions] = { + conditions: wallet.conditions, + balance: wallet.balance + } + }, + sindexDAL: { + getAvailableForConditions: null + } +} + +export class QuickSynchronizer { + + constructor(private blockchain:DuniterBlockchain, private conf: any, private dal: any, private logger: any) { + } + + async saveBlocksInMainBranch(blocks: BlockDTO[]): Promise<void> { + // VERY FIRST: parameters, otherwise we compute wrong variables such as UDTime + if (blocks[0].number == 0) { + await this.blockchain.saveParametersForRoot(blocks[0], this.conf, this.dal) + } + // Helper to retrieve a block with local cache + const getBlock = (number: number): BlockDTO => { + const firstLocalNumber = blocks[0].number; + if (number >= firstLocalNumber) { + let offset = number - firstLocalNumber; + return Q(blocks[offset]); + } + return this.dal.getBlock(number); + }; + const getBlockByNumberAndHash = async (number: number, hash: string): Promise<BlockDTO> => { + const block = await getBlock(number); + if (!block || block.hash != hash) { + throw 'Block #' + [number, hash].join('-') + ' not found neither in DB nor in applying blocks'; + } + return block; + } + for (const block of blocks) { + block.fork = false; + } + // Transactions recording + await this.updateTransactionsForBlocks(blocks, getBlockByNumberAndHash); + await this.dal.blockDAL.saveBunch(blocks); + await DuniterBlockchain.pushStatsForBlocks(blocks, this.dal); + } + + private async updateTransactionsForBlocks(blocks: BlockDTO[], getBlockByNumberAndHash: (number: number, hash: string) => Promise<BlockDTO>): Promise<any> { + let txs: DBTransaction[] = []; + for (const block of blocks) { + const newOnes: DBTransaction[] = []; + for (const tx of block.transactions) { + const [number, hash] = tx.blockstamp.split('-') + const refBlock: BlockDTO = (await getBlockByNumberAndHash(parseInt(number), hash)) + // We force the usage of the reference block's currency + tx.currency = refBlock.currency + tx.hash = tx.getHash() + const dbTx: DBTransaction = DBTransaction.fromTransactionDTO(tx, refBlock.medianTime, true, false, refBlock.number, refBlock.medianTime) + newOnes.push(dbTx) + } + txs = txs.concat(newOnes); + } + return this.dal.updateTransactions(txs); + } + + async quickApplyBlocks(blocks:BlockDTO[], to: number | null): Promise<void> { + + sync_memoryDAL.sindexDAL = { getAvailableForConditions: this.dal.sindexDAL.getAvailableForConditions } + let blocksToSave: BlockDTO[] = []; + + for (const block of blocks) { + sync_allBlocks.push(block); + + // The new kind of object stored + const dto = BlockDTO.fromJSONObject(block) + + if (block.number == 0) { + sync_currConf = Block.statics.getConf(block); + } + + if (block.number != to) { + blocksToSave.push(dto); + const index:any = Indexer.localIndex(dto, sync_currConf); + const local_iindex = Indexer.iindex(index); + const local_cindex = Indexer.cindex(index); + const local_sindex = Indexer.sindex(index); + const local_mindex = Indexer.mindex(index); + sync_iindex = sync_iindex.concat(local_iindex); + sync_cindex = sync_cindex.concat(local_cindex); + sync_mindex = sync_mindex.concat(local_mindex); + + const HEAD = await Indexer.quickCompleteGlobalScope(block, sync_currConf, sync_bindex, sync_iindex, sync_mindex, sync_cindex, { + getBlock: (number: number) => { + return Promise.resolve(sync_allBlocks[number]); + }, + getBlockByBlockstamp: (blockstamp: string) => { + return Promise.resolve(sync_allBlocks[parseInt(blockstamp)]); + } + }); + sync_bindex.push(HEAD); + + // Remember expiration dates + for (const entry of index) { + if (entry.op === 'CREATE' && (entry.expires_on || entry.revokes_on)) { + sync_expires.push(entry.expires_on || entry.revokes_on); + } + } + sync_expires = _.uniq(sync_expires); + + await this.blockchain.createNewcomers(local_iindex, this.dal, this.logger) + + if (block.dividend + || block.joiners.length + || block.actives.length + || block.revoked.length + || block.excluded.length + || block.certifications.length + || block.transactions.length + || block.medianTime >= sync_nextExpiring) { + // logger.warn('>> Block#%s', block.number) + + for (let i = 0; i < sync_expires.length; i++) { + let expire = sync_expires[i]; + if (block.medianTime > expire) { + sync_expires.splice(i, 1); + i--; + } + } + let currentNextExpiring = sync_nextExpiring + sync_nextExpiring = sync_expires.reduce((max, value) => max ? Math.min(max, value) : value, sync_nextExpiring); + const nextExpiringChanged = currentNextExpiring !== sync_nextExpiring + + // Fills in correctly the SINDEX + await Promise.all(_.where(sync_sindex.concat(local_sindex), { op: 'UPDATE' }).map(async (entry: any) => { + if (!entry.conditions) { + const src = await this.dal.sindexDAL.getSource(entry.identifier, entry.pos); + entry.conditions = src.conditions; + } + })) + + // Flush the INDEX (not bindex, which is particular) + await this.dal.mindexDAL.insertBatch(sync_mindex); + await this.dal.iindexDAL.insertBatch(sync_iindex); + await this.dal.sindexDAL.insertBatch(sync_sindex); + await this.dal.cindexDAL.insertBatch(sync_cindex); + sync_mindex = []; + sync_iindex = []; + sync_cindex = []; + sync_sindex = local_sindex; + + sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGenDividend(HEAD, this.dal)); + sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGarbageSmallAccounts(HEAD, sync_sindex, sync_memoryDAL)); + if (nextExpiringChanged) { + sync_cindex = sync_cindex.concat(await Indexer.ruleIndexGenCertificationExpiry(HEAD, this.dal)); + sync_mindex = sync_mindex.concat(await Indexer.ruleIndexGenMembershipExpiry(HEAD, this.dal)); + sync_iindex = sync_iindex.concat(await Indexer.ruleIndexGenExclusionByMembership(HEAD, sync_mindex, this.dal)); + sync_iindex = sync_iindex.concat(await Indexer.ruleIndexGenExclusionByCertificatons(HEAD, sync_cindex, local_iindex, this.conf, this.dal)); + sync_mindex = sync_mindex.concat(await Indexer.ruleIndexGenImplicitRevocation(HEAD, this.dal)); + } + // Update balances with UD + local garbagings + await this.blockchain.updateWallets(sync_sindex, sync_memoryDAL) + + // --> Update links + await this.dal.updateWotbLinks(local_cindex.concat(sync_cindex)); + + // Flush the INDEX again + await this.dal.mindexDAL.insertBatch(sync_mindex); + await this.dal.iindexDAL.insertBatch(sync_iindex); + await this.dal.sindexDAL.insertBatch(sync_sindex); + await this.dal.cindexDAL.insertBatch(sync_cindex); + sync_mindex = []; + sync_iindex = []; + sync_cindex = []; + sync_sindex = []; + + // Create/Update nodes in wotb + await this.blockchain.updateMembers(block, this.dal) + } + + // Trim the bindex + sync_bindexSize = [ + block.issuersCount, + block.issuersFrame, + this.conf.medianTimeBlocks, + this.conf.dtDiffEval, + blocks.length + ].reduce((max, value) => { + return Math.max(max, value); + }, 0); + + if (sync_bindexSize && sync_bindex.length >= 2 * sync_bindexSize) { + // We trim it, not necessary to store it all (we already store the full blocks) + sync_bindex.splice(0, sync_bindexSize); + + // Process triming continuously to avoid super long ending of sync + await this.dal.trimIndexes(sync_bindex[0].number); + } + } else { + + if (blocksToSave.length) { + await this.saveBlocksInMainBranch(blocksToSave); + } + blocksToSave = []; + + // Save the INDEX + await this.dal.bindexDAL.insertBatch(sync_bindex); + await this.dal.mindexDAL.insertBatch(sync_mindex); + await this.dal.iindexDAL.insertBatch(sync_iindex); + await this.dal.sindexDAL.insertBatch(sync_sindex); + await this.dal.cindexDAL.insertBatch(sync_cindex); + + // Save the intermediary table of wallets + const conditions = _.keys(sync_memoryWallets) + const nonEmptyKeys = _.filter(conditions, (k: any) => sync_memoryWallets[k] && sync_memoryWallets[k].balance > 0) + const walletsToRecord = nonEmptyKeys.map((k: any) => sync_memoryWallets[k]) + await this.dal.walletDAL.insertBatch(walletsToRecord) + + // Last block: cautious mode to trigger all the INDEX expiry mechanisms + const { index, HEAD } = await this.blockchain.checkBlock(dto, constants.WITH_SIGNATURES_AND_POW, this.conf, this.dal) + await this.blockchain.pushTheBlock(dto, index, HEAD, this.conf, this.dal, this.logger) + + // Clean temporary variables + sync_bindex = []; + sync_iindex = []; + sync_mindex = []; + sync_cindex = []; + sync_sindex = []; + sync_bindexSize = 0; + sync_allBlocks = []; + sync_expires = []; + sync_nextExpiring = 0; + // sync_currConf = {}; + } + } + if (blocksToSave.length) { + await this.saveBlocksInMainBranch(blocksToSave); + } + } +} diff --git a/app/lib/computation/blockchainContext.js b/app/lib/computation/blockchainContext.js deleted file mode 100644 index 03c554480d2c7c88419368a6a16d5233e7236c73..0000000000000000000000000000000000000000 --- a/app/lib/computation/blockchainContext.js +++ /dev/null @@ -1,145 +0,0 @@ -"use strict"; -const _ = require('underscore'); -const co = require('co'); -const indexer = require('../indexer'); -const constants = require('../constants'); -const Block = require('../entity/block'); - -module.exports = () => { return new BlockchainContext() }; - -function BlockchainContext() { - - const that = this; - let conf, dal, logger, blockchain, quickSynchronizer - - /** - * 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 = Promise.resolve(); - - /** - * 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), 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 issuer The issuer we want to get the difficulty level. - */ - this.getIssuerPersonalizedDifficulty = (issuer) => co(function *() { - const local_vHEAD = yield that.getvHeadCopy({ issuer }); - yield indexer.preparePersonalizedPoW(local_vHEAD, vHEAD_1, dal.range, conf); - return local_vHEAD.issuerDiff; - }); - - this.setConfDAL = (newConf, newDAL, theBlockchain, theQuickSynchronizer) => { - dal = newDAL; - conf = newConf; - blockchain = theBlockchain - quickSynchronizer = theQuickSynchronizer - logger = require('../logger')(dal.profile); - }; - - this.checkBlock = (block, withPoWAndSignature) => blockchain.checkBlock(block, withPoWAndSignature, conf, dal) - - this.addBlock = (obj, index, HEAD) => co(function*() { - const block = yield blockchain.pushTheBlock(obj, index, HEAD, conf, dal, logger) - vHEAD_1 = vHEAD = HEADrefreshed = null - return block - }) - - this.addSideBlock = (obj) => blockchain.pushSideBlock(obj, dal, logger) - - this.revertCurrentBlock = () => co(function *() { - const head_1 = yield dal.bindexDAL.head(1); - logger.debug('Reverting block #%s...', head_1.number); - const res = yield blockchain.revertBlock(head_1.number, head_1.hash, dal) - logger.debug('Reverted block #%s', head_1.number); - // Invalidates the head, since it has changed. - yield refreshHead(); - return res; - }); - - this.applyNextAvailableFork = () => co(function *() { - const current = yield that.current(); - logger.debug('Find next potential block #%s...', current.number + 1); - const forks = yield dal.getForkBlocksFollowing(current); - if (!forks.length) { - throw constants.ERRORS.NO_POTENTIAL_FORK_AS_NEXT; - } - const block = forks[0]; - const { index, HEAD } = yield that.checkBlock(block, constants.WITH_SIGNATURES_AND_POW); - yield that.addBlock(block, index, HEAD); - logger.debug('Applied block #%s', block.number); - }); - - this.current = () => dal.getCurrentBlockOrNull(); - - this.checkHaveEnoughLinks = (target, newLinks) => co(function*() { - const links = yield dal.getValidLinksTo(target); - let count = links.length; - if (newLinks[target] && newLinks[target].length) - count += newLinks[target].length; - if (count < conf.sigQty) - throw 'Key ' + target + ' does not have enough links (' + count + '/' + conf.sigQty + ')'; - }); - - this.quickApplyBlocks = (blocks, to) => quickSynchronizer.quickApplyBlocks(blocks, to) -} diff --git a/app/lib/computation/quickSync.js b/app/lib/computation/quickSync.js deleted file mode 100644 index 0af47eb2548ff63e8bf6bdf443d690947fef0133..0000000000000000000000000000000000000000 --- a/app/lib/computation/quickSync.js +++ /dev/null @@ -1,265 +0,0 @@ -"use strict" - -const Q = require('q'); -const _ = require('underscore') -const co = require('co') -const indexer = require('../indexer') -const constants = require('../constants') -const Block = require('../entity/block') -const Transaction = require('../entity/transaction') -const DuniterBlockchain = require('../blockchain/DuniterBlockchain').DuniterBlockchain - -module.exports = (blockchain, conf, dal, logger) => { - - let sync_bindex = []; - let sync_iindex = []; - let sync_mindex = []; - let sync_cindex = []; - let sync_sindex = []; - let sync_bindexSize = 0; - let sync_allBlocks = []; - let sync_expires = []; - let sync_nextExpiring = 0; - let sync_currConf = {}; - const sync_memoryWallets = {} - const sync_memoryDAL = { - getWallet: (conditions) => Promise.resolve(sync_memoryWallets[conditions] || { conditions, balance: 0 }), - saveWallet: (wallet) => co(function*() { - // Make a copy - sync_memoryWallets[wallet.conditions] = { - conditions: wallet.conditions, - balance: wallet.balance - } - }) - } - - const saveBlocksInMainBranch = (blocks) => co(function *() { - // VERY FIRST: parameters, otherwise we compute wrong variables such as UDTime - if (blocks[0].number == 0) { - yield blockchain.saveParametersForRoot(blocks[0], conf, dal) - } - // Helper to retrieve a block with local cache - const getBlock = (number) => { - const firstLocalNumber = blocks[0].number; - if (number >= firstLocalNumber) { - let offset = number - firstLocalNumber; - return Q(blocks[offset]); - } - return dal.getBlock(number); - }; - const getBlockByNumberAndHash = (number, hash) => co(function*() { - const block = yield getBlock(number); - if (!block || block.hash != hash) { - throw 'Block #' + [number, hash].join('-') + ' not found neither in DB nor in applying blocks'; - } - return block; - }); - for (const block of blocks) { - block.fork = false; - } - // Transactions recording - yield updateTransactionsForBlocks(blocks, getBlockByNumberAndHash); - yield dal.blockDAL.saveBunch(blocks); - yield DuniterBlockchain.pushStatsForBlocks(blocks, dal); - }); - - function updateTransactionsForBlocks(blocks, getBlockByNumberAndHash) { - return co(function *() { - let txs = []; - for (const block of blocks) { - const newOnes = []; - for (const tx of block.transactions) { - _.extend(tx, { - block_number: block.number, - time: block.medianTime, - currency: block.currency, - written: true, - removed: false - }); - const sp = tx.blockstamp.split('-'); - tx.blockstampTime = (yield getBlockByNumberAndHash(sp[0], sp[1])).medianTime; - const txEntity = new Transaction(tx); - txEntity.computeAllHashes(); - newOnes.push(txEntity); - } - txs = txs.concat(newOnes); - } - return dal.updateTransactions(txs); - }) - } - - const quickApplyBlocks = (blocks, to) => co(function*() { - - sync_memoryDAL.sindexDAL = { getAvailableForConditions: dal.sindexDAL.getAvailableForConditions } - let blocksToSave = []; - - for (const block of blocks) { - sync_allBlocks.push(block); - - if (block.number == 0) { - sync_currConf = Block.statics.getConf(block); - } - - if (block.number != to) { - blocksToSave.push(block); - const index = indexer.localIndex(block, sync_currConf); - const local_iindex = indexer.iindex(index); - const local_cindex = indexer.cindex(index); - const local_sindex = indexer.sindex(index); - const local_mindex = indexer.mindex(index); - sync_iindex = sync_iindex.concat(local_iindex); - sync_cindex = sync_cindex.concat(local_cindex); - sync_mindex = sync_mindex.concat(local_mindex); - - const HEAD = yield indexer.quickCompleteGlobalScope(block, sync_currConf, sync_bindex, sync_iindex, sync_mindex, sync_cindex, { - getBlock: (number) => { - return Promise.resolve(sync_allBlocks[number]); - }, - getBlockByBlockstamp: (blockstamp) => { - return Promise.resolve(sync_allBlocks[parseInt(blockstamp)]); - } - }); - sync_bindex.push(HEAD); - - // Remember expiration dates - for (const entry of index) { - if (entry.op === 'CREATE' && (entry.expires_on || entry.revokes_on)) { - sync_expires.push(entry.expires_on || entry.revokes_on); - } - } - sync_expires = _.uniq(sync_expires); - - yield blockchain.createNewcomers(local_iindex, dal, logger) - - if (block.dividend - || block.joiners.length - || block.actives.length - || block.revoked.length - || block.excluded.length - || block.certifications.length - || block.transactions.length - || block.medianTime >= sync_nextExpiring) { - // logger.warn('>> Block#%s', block.number) - - for (let i = 0; i < sync_expires.length; i++) { - let expire = sync_expires[i]; - if (block.medianTime > expire) { - sync_expires.splice(i, 1); - i--; - } - } - let currentNextExpiring = sync_nextExpiring - sync_nextExpiring = sync_expires.reduce((max, value) => max ? Math.min(max, value) : value, sync_nextExpiring); - const nextExpiringChanged = currentNextExpiring !== sync_nextExpiring - - // Fills in correctly the SINDEX - yield _.where(sync_sindex.concat(local_sindex), { op: 'UPDATE' }).map((entry) => co(function*() { - if (!entry.conditions) { - const src = yield dal.sindexDAL.getSource(entry.identifier, entry.pos); - entry.conditions = src.conditions; - } - })) - - // Flush the INDEX (not bindex, which is particular) - yield dal.mindexDAL.insertBatch(sync_mindex); - yield dal.iindexDAL.insertBatch(sync_iindex); - yield dal.sindexDAL.insertBatch(sync_sindex); - yield dal.cindexDAL.insertBatch(sync_cindex); - sync_mindex = []; - sync_iindex = []; - sync_cindex = []; - sync_sindex = local_sindex; - - sync_sindex = sync_sindex.concat(yield indexer.ruleIndexGenDividend(HEAD, dal)); - sync_sindex = sync_sindex.concat(yield indexer.ruleIndexGarbageSmallAccounts(HEAD, sync_sindex, sync_memoryDAL)); - if (nextExpiringChanged) { - sync_cindex = sync_cindex.concat(yield indexer.ruleIndexGenCertificationExpiry(HEAD, dal)); - sync_mindex = sync_mindex.concat(yield indexer.ruleIndexGenMembershipExpiry(HEAD, dal)); - sync_iindex = sync_iindex.concat(yield indexer.ruleIndexGenExclusionByMembership(HEAD, sync_mindex, dal)); - sync_iindex = sync_iindex.concat(yield indexer.ruleIndexGenExclusionByCertificatons(HEAD, sync_cindex, local_iindex, conf, dal)); - sync_mindex = sync_mindex.concat(yield indexer.ruleIndexGenImplicitRevocation(HEAD, dal)); - } - // Update balances with UD + local garbagings - yield blockchain.updateWallets(sync_sindex, sync_memoryDAL) - - // --> Update links - yield dal.updateWotbLinks(local_cindex.concat(sync_cindex)); - - // Flush the INDEX again - yield dal.mindexDAL.insertBatch(sync_mindex); - yield dal.iindexDAL.insertBatch(sync_iindex); - yield dal.sindexDAL.insertBatch(sync_sindex); - yield dal.cindexDAL.insertBatch(sync_cindex); - sync_mindex = []; - sync_iindex = []; - sync_cindex = []; - sync_sindex = []; - - // Create/Update nodes in wotb - yield blockchain.updateMembers(block, dal) - } - - // Trim the bindex - sync_bindexSize = [ - block.issuersCount, - block.issuersFrame, - conf.medianTimeBlocks, - conf.dtDiffEval, - blocks.length - ].reduce((max, value) => { - return Math.max(max, value); - }, 0); - - if (sync_bindexSize && sync_bindex.length >= 2 * sync_bindexSize) { - // We trim it, not necessary to store it all (we already store the full blocks) - sync_bindex.splice(0, sync_bindexSize); - - // Process triming continuously to avoid super long ending of sync - yield dal.trimIndexes(sync_bindex[0].number); - } - } else { - - if (blocksToSave.length) { - yield saveBlocksInMainBranch(blocksToSave); - } - blocksToSave = []; - - // Save the INDEX - yield dal.bindexDAL.insertBatch(sync_bindex); - yield dal.mindexDAL.insertBatch(sync_mindex); - yield dal.iindexDAL.insertBatch(sync_iindex); - yield dal.sindexDAL.insertBatch(sync_sindex); - yield dal.cindexDAL.insertBatch(sync_cindex); - - // Save the intermediary table of wallets - const conditions = _.keys(sync_memoryWallets) - const nonEmptyKeys = _.filter(conditions, (k) => sync_memoryWallets[k] && sync_memoryWallets[k].balance > 0) - const walletsToRecord = nonEmptyKeys.map((k) => sync_memoryWallets[k]) - yield dal.walletDAL.insertBatch(walletsToRecord) - - // Last block: cautious mode to trigger all the INDEX expiry mechanisms - const { index, HEAD } = yield blockchain.checkBlock(block, constants.WITH_SIGNATURES_AND_POW, conf, dal) - yield blockchain.pushTheBlock(block, index, HEAD, conf, dal, logger) - - // Clean temporary variables - sync_bindex = []; - sync_iindex = []; - sync_mindex = []; - sync_cindex = []; - sync_sindex = []; - sync_bindexSize = 0; - sync_allBlocks = []; - sync_expires = []; - sync_nextExpiring = 0; - sync_currConf = {}; - } - } - if (blocksToSave.length) { - yield saveBlocksInMainBranch(blocksToSave); - } - }) - - return { - saveBlocksInMainBranch, quickApplyBlocks - } -} diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js index 9886dbb14aaa86b716eea876cb2a14da1a58ac2f..ad57c658c14da55064045ce88df9a7ed25f0d1f6 100644 --- a/app/lib/dal/fileDAL.js +++ b/app/lib/dal/fileDAL.js @@ -3,7 +3,7 @@ const Q = require('q'); const co = require('co'); const _ = require('underscore'); const common = require('duniter-common'); -const indexer = require('../indexer'); +const indexer = require('../indexer').Indexer const logger = require('../logger')('filedal'); const Configuration = require('../entity/configuration'); const Merkle = require('../entity/merkle'); diff --git a/app/lib/dal/sqliteDAL/AbstractIndex.js b/app/lib/dal/sqliteDAL/AbstractIndex.js index 0187fc266b22c0e250b4bb9bb6b055559c2a4083..1078a8257174c471fee076b16a59e935d1b3f3cb 100644 --- a/app/lib/dal/sqliteDAL/AbstractIndex.js +++ b/app/lib/dal/sqliteDAL/AbstractIndex.js @@ -4,7 +4,7 @@ const _ = require('underscore'); const co = require('co'); -const indexer = require('../../indexer'); +const indexer = require('../../indexer').Indexer module.exports = AbstractIndex; diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.js b/app/lib/dal/sqliteDAL/index/CIndexDAL.js index 37d2de8594a78261491a390bd412b48c79bb0f31..dde0e93940ff2f93a34d85d5dff31a5d8313017d 100644 --- a/app/lib/dal/sqliteDAL/index/CIndexDAL.js +++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.js @@ -5,7 +5,7 @@ const co = require('co'); const constants = require('./../../../constants'); const common = require('duniter-common'); -const indexer = require('../../../indexer'); +const indexer = require('../../../indexer').Indexer const AbstractSQLite = require('./../AbstractSQLite'); const AbstractIndex = require('./../AbstractIndex'); diff --git a/app/lib/dal/sqliteDAL/index/IIndexDAL.js b/app/lib/dal/sqliteDAL/index/IIndexDAL.js index 4dc35e6f036c8748d004f89ecc5b6e6eec9c4cd6..4030b5905c4159cc030b79304b058882d1d9502c 100644 --- a/app/lib/dal/sqliteDAL/index/IIndexDAL.js +++ b/app/lib/dal/sqliteDAL/index/IIndexDAL.js @@ -4,7 +4,7 @@ const co = require('co'); const _ = require('underscore'); -const indexer = require('../../../indexer'); +const indexer = require('../../../indexer').Indexer const AbstractSQLite = require('./../AbstractSQLite'); const AbstractIndex = require('./../AbstractIndex'); diff --git a/app/lib/dal/sqliteDAL/index/MIndexDAL.js b/app/lib/dal/sqliteDAL/index/MIndexDAL.js index 572a9b32733613d9a2c8c4c88f97c5601cc5a68a..77faa633d0d18fc9fc5af5ddbf859efb8ae0e9cd 100644 --- a/app/lib/dal/sqliteDAL/index/MIndexDAL.js +++ b/app/lib/dal/sqliteDAL/index/MIndexDAL.js @@ -3,7 +3,7 @@ */ const co = require('co'); -const indexer = require('../../../indexer'); +const indexer = require('../../../indexer').Indexer const AbstractSQLite = require('./../AbstractSQLite'); const AbstractIndex = require('./../AbstractIndex'); diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.js b/app/lib/dal/sqliteDAL/index/SIndexDAL.js index 948eade98d6e357a187e54a6c3c0b2b01666c05e..f5e5e84136530d343a104f4d19db685ce1bb9bcf 100644 --- a/app/lib/dal/sqliteDAL/index/SIndexDAL.js +++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.js @@ -5,7 +5,7 @@ const _ = require('underscore'); const co = require('co'); const common = require('duniter-common'); -const indexer = require('../../../indexer'); +const indexer = require('../../../indexer').Indexer const constants = require('../../../constants'); const AbstractSQLite = require('./../AbstractSQLite'); const AbstractIndex = require('./../AbstractIndex'); diff --git a/app/lib/db/DBBlock.ts b/app/lib/db/DBBlock.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad283135a58c0f0f93528d848061125ab3bf8d75 --- /dev/null +++ b/app/lib/db/DBBlock.ts @@ -0,0 +1,74 @@ +import {BlockDTO} from "../dto/BlockDTO" +import {TransactionDTO} from "../dto/TransactionDTO" + +export class DBBlock { + + version: number + number: number + currency: string + hash: string + inner_hash: string + signature: string + previousHash: string + issuer: string + previousIssuer: string + time: number + powMin: number + unitbase: number + membersCount: number + issuersCount: number + issuersFrame: number + issuersFrameVar: number + identities: string[] + joiners: string[] + actives: string[] + leavers: string[] + revoked: string[] + excluded: string[] + certifications: string[] + transactions: TransactionDTO[] + medianTime: number + nonce: number + fork: boolean + parameters: string + monetaryMass: number + dividend: number | null + + constructor( + ) { + } + + static fromBlockDTO(b:BlockDTO) { + const dbb = new DBBlock() + dbb.version = b.version + dbb.number = b.number + dbb.currency = b.currency + dbb.hash = b.hash + dbb.previousHash = b.previousHash + dbb.issuer = b.issuer + dbb.previousIssuer = b.previousIssuer + dbb.dividend = b.dividend + dbb.time = b.time + dbb.powMin = b.powMin + dbb.unitbase = b.unitbase + dbb.membersCount = b.membersCount + dbb.issuersCount = b.issuersCount + dbb.issuersFrame = b.issuersFrame + dbb.issuersFrameVar = b.issuersFrameVar + dbb.identities = b.identities + dbb.joiners = b.joiners + dbb.actives = b.actives + dbb.leavers = b.leavers + dbb.revoked = b.revoked + dbb.excluded = b.excluded + dbb.certifications = b.certifications + dbb.transactions = b.transactions + dbb.medianTime = b.medianTime + dbb.fork = b.fork + dbb.parameters = b.parameters + dbb.inner_hash = b.inner_hash + dbb.signature = b.signature + dbb.nonce = b.nonce + return dbb + } +} \ No newline at end of file diff --git a/app/lib/db/DBHead.ts b/app/lib/db/DBHead.ts new file mode 100644 index 0000000000000000000000000000000000000000..2154b015f299e07ed6ebe05af06173ab558c3603 --- /dev/null +++ b/app/lib/db/DBHead.ts @@ -0,0 +1,38 @@ +export class DBHead { + + // TODO: some properties are not registered in the DB, we should create another class + + version: number + currency: string | null + bsize: number + avgBlockSize: number + udTime: number + udReevalTime: number + massReeval: number + mass: number + hash: string + previousHash: string | null + previousIssuer: string | null + issuer: string + time: number + medianTime: number + number: number + powMin: number + diffNumber: number + issuersCount: number + issuersFrame: number + issuersFrameVar: number + dtDiffEval: number + issuerDiff: number + powZeros: number + powRemainder: number + speed: number + unitBase: number + membersCount: number + dividend: number + new_dividend: number | null + issuerIsMember: boolean + + constructor( + ) {} +} \ No newline at end of file diff --git a/app/lib/db/DBTransaction.ts b/app/lib/db/DBTransaction.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a012e98df1c6c02ca85dd8b667fe0593a918d2c --- /dev/null +++ b/app/lib/db/DBTransaction.ts @@ -0,0 +1,58 @@ +import {TransactionDTO} from "../dto/TransactionDTO" + +export class DBTransaction extends TransactionDTO { + + constructor( + public version: number, + public currency: string, + public locktime: number, + public hash: string, + public blockstamp: string, + public issuers: string[], + public inputs: string[], + public outputs: string[], + public unlocks: string[], + public signatures: string[], + public comment: string, + public blockstampTime: number, + public written: boolean, + public removed: boolean, + public block_number: number, + public time: number, + ) { + super( + version, + currency, + locktime, + hash, + blockstamp, + issuers, + inputs, + outputs, + unlocks, + signatures, + comment + ) + } + + static fromTransactionDTO(dto:TransactionDTO, blockstampTime:number, written: boolean, removed: boolean, block_number:number, block_medianTime:number) { + return new DBTransaction( + dto.version, + dto.currency, + dto.locktime, + dto.hash, + dto.blockstamp, + dto.issuers, + dto.inputs, + dto.outputs, + dto.unlocks, + dto.signatures, + dto.comment || "", + blockstampTime, + written, + removed, + block_number, + block_medianTime + ) + } +} \ No newline at end of file diff --git a/app/lib/dto/BlockDTO.ts b/app/lib/dto/BlockDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..d966371fe052faac6ab6c1327d2d1ccde89f75fb --- /dev/null +++ b/app/lib/dto/BlockDTO.ts @@ -0,0 +1,144 @@ +import {TransactionDTO} from "./TransactionDTO" +export class BlockDTO { + + version: number + number: number + currency: string + hash: string + inner_hash: string + previousHash: string + issuer: string + previousIssuer: string + dividend: number + time: number + powMin: number + unitbase: number + membersCount: number + issuersCount: number + issuersFrame: number + issuersFrameVar: number + identities: string[] + joiners: string[] + actives: string[] + leavers: string[] + revoked: string[] + excluded: string[] + certifications: string[] + transactions: TransactionDTO[] + medianTime: number + nonce: number + fork: boolean + parameters: string + signature: string + + constructor( +) {} + + getInlineIdentity(pubkey:string): string | null { + let i = 0; + let found = null; + while (!found && i < this.identities.length) { + if (this.identities[i].match(new RegExp('^' + pubkey))) + found = this.identities[i]; + i++; + } + return found; + } + + getSignedPart() { + return "InnerHash: " + this.inner_hash + "\n" + + "Nonce: " + this.nonce + "\n" + } + + getRawInnerPart() { + let raw = ""; + raw += "Version: " + this.version + "\n"; + raw += "Type: Block\n"; + raw += "Currency: " + this.currency + "\n"; + raw += "Number: " + this.number + "\n"; + raw += "PoWMin: " + this.powMin + "\n"; + raw += "Time: " + this.time + "\n"; + raw += "MedianTime: " + this.medianTime + "\n"; + if (this.dividend) + raw += "UniversalDividend: " + this.dividend + "\n"; + raw += "UnitBase: " + this.unitbase + "\n"; + raw += "Issuer: " + this.issuer + "\n"; + raw += "IssuersFrame: " + this.issuersFrame + "\n"; + raw += "IssuersFrameVar: " + this.issuersFrameVar + "\n"; + raw += "DifferentIssuersCount: " + this.issuersCount + "\n"; + if(this.previousHash) + raw += "PreviousHash: " + this.previousHash + "\n"; + if(this.previousIssuer) + raw += "PreviousIssuer: " + this.previousIssuer + "\n"; + if(this.parameters) + raw += "Parameters: " + this.parameters + "\n"; + raw += "MembersCount: " + this.membersCount + "\n"; + raw += "Identities:\n"; + for (const idty of (this.identities || [])){ + raw += idty + "\n"; + } + raw += "Joiners:\n"; + for (const joiner of (this.joiners || [])){ + raw += joiner + "\n"; + } + raw += "Actives:\n"; + for (const active of (this.actives || [])){ + raw += active + "\n"; + } + raw += "Leavers:\n"; + for (const leaver of (this.leavers || [])){ + raw += leaver + "\n"; + } + raw += "Revoked:\n"; + for (const revoked of (this.revoked || [])){ + raw += revoked + "\n"; + } + raw += "Excluded:\n"; + for (const excluded of (this.excluded || [])){ + raw += excluded + "\n"; + } + raw += "Certifications:\n"; + for (const cert of (this.certifications || [])){ + raw += cert + "\n"; + } + raw += "Transactions:\n"; + for (const tx of (this.transactions || [])){ + raw += tx.getCompactVersion(); + } + return raw + } + + static fromJSONObject(obj:any) { + const dto = new BlockDTO() + dto.version = parseInt(obj.version) + dto.number = parseInt(obj.number) + dto.currency = obj.currency + dto.hash = obj.hash + dto.inner_hash = obj.inner_hash + dto.previousHash = obj.previousHash + dto.issuer = obj.issuer + dto.previousIssuer = obj.previousIssuer + dto.dividend = obj.dividend || null + dto.time = parseInt(obj.time) + dto.powMin = parseInt(obj.powMin) + dto.unitbase = parseInt(obj.unitbase) + dto.membersCount = parseInt(obj.membersCount) + dto.issuersCount = parseInt(obj.issuersCount) + dto.issuersFrame = parseInt(obj.issuersFrame) + dto.issuersFrameVar = parseInt(obj.issuersFrameVar) + dto.identities = obj.identities + dto.joiners = obj.joiners + dto.actives = obj.actives + dto.leavers = obj.leavers + dto.revoked = obj.revoked + dto.excluded = obj.excluded + dto.certifications = obj.certifications + dto.transactions = obj.transactions.map(TransactionDTO.fromJSONObject) + dto.medianTime = parseInt(obj.medianTime) + dto.fork = !!obj.fork + dto.parameters = obj.parameters + dto.signature = obj.signature + dto.nonce = parseInt(obj.nonce) + return dto + } +} \ No newline at end of file diff --git a/app/lib/dto/CertificationDTO.ts b/app/lib/dto/CertificationDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..aca3cea3f8363dc8d6f438b961daa7c370d02a30 --- /dev/null +++ b/app/lib/dto/CertificationDTO.ts @@ -0,0 +1,14 @@ +export class CertificationDTO { + + constructor( + public pubkey: string, + public to: string, + public block_number: number, + public sig: string + ) {} + + static fromInline(inline:string): CertificationDTO { + const [pubkey, to, block_number, sig]: string[] = inline.split(':') + return new CertificationDTO(pubkey, to, parseInt(block_number), sig) + } +} \ No newline at end of file diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..66cfca0d7be95ec4d1066ddc38f501708df6f528 --- /dev/null +++ b/app/lib/dto/ConfDTO.ts @@ -0,0 +1,35 @@ +export class ConfDTO { + + constructor( + public currency: string, + public endpoints: string[], + public rmEndpoints: string[], + public upInterval: number, + public c: number, + public dt: number, + public dtReeval: number, + public dtDiffEval: number, + public ud0: number, + public udTime0: number, + public udReevalTime0: number, + public stepMax: number, + public sigPeriod: number, + public msPeriod: number, + public sigValidity: number, + public msValidity: number, + public sigQty: number, + public sigStock: number, + public xpercent: number, + public percentRot: number, + public powDelay: number, + public avgGenTime: number, + public medianTimeBlocks: number, + public httplogs: boolean, + public timeout: number, + public isolate: boolean, + public forksize: number, + public idtyWindow: number, + public msWindow: number, + public sigWindow: number, +) {} +} \ No newline at end of file diff --git a/app/lib/dto/IdentityDTO.ts b/app/lib/dto/IdentityDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7485752387b81d3d5b71bd6140016dbe8ddc88c --- /dev/null +++ b/app/lib/dto/IdentityDTO.ts @@ -0,0 +1,32 @@ + +import {hashf} from "../common" + +export class IdentityDTO { + + currency:string + + constructor( + public pubkey: string, + public sig: string, + public buid: string, + public uid: string + ) {} + + get hash() { + return this.getTargetHash() + } + + private getTargetHash() { + return hashf(this.uid + this.buid + this.pubkey) + } + + static fromInline(inline:string): IdentityDTO { + const [pubkey, sig, buid, uid] = inline.split(':') + return new IdentityDTO( + pubkey, + sig, + buid, + uid + ) + } +} \ No newline at end of file diff --git a/app/lib/dto/RevocationDTO.ts b/app/lib/dto/RevocationDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..be0be61b170c246a68b2a2ec29cabd7017b06da1 --- /dev/null +++ b/app/lib/dto/RevocationDTO.ts @@ -0,0 +1,12 @@ +export class RevocationDTO { + + constructor( + public pubkey: string, + public sig: string + ) {} + + static fromInline(inline:string): RevocationDTO { + const [pubkey, sig] = inline.split(':') + return new RevocationDTO(pubkey, sig) + } +} \ No newline at end of file diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts new file mode 100644 index 0000000000000000000000000000000000000000..2bd4ea0d2d10a4b4755c8676df3040975153e6df --- /dev/null +++ b/app/lib/dto/TransactionDTO.ts @@ -0,0 +1,150 @@ +import {hashf} from "../common" + +export class InputDTO { + constructor( + public amount: number, + public base: number, + public type: string, + public identifier: string, + public pos: number, + public raw: string + ) {} +} + +export class OutputDTO { + constructor( + public amount: number, + public base: number, + public conditions: string, + public raw: string + ) {} +} + +export class TransactionDTO { + + constructor( + public version: number, + public currency: string, + public locktime: number, + public hash: string, + public blockstamp: string, + public issuers: string[], + public inputs: string[], + public outputs: string[], + public unlocks: string[], + public signatures: string[], + public comment?: string + ) { + // Compute the hash if not given + if (!hash) { + this.hash = this.getHash() + } + } + + getHash() { + const raw = TransactionDTO.toRAW(this) + return hashf(raw) + } + + getRawTxNoSig() { + return TransactionDTO.toRAW(this, true) + } + + inputsAsObjects(): InputDTO[] { + return this.inputs.map(input => { + const [amount, base, type, identifier, pos] = input.split(':') + return new InputDTO( + parseInt(amount), + parseInt(base), + type, + identifier, + parseInt(pos), + input + ) + }) + } + + outputsAsObjects(): OutputDTO[] { + return this.outputs.map(output => { + const [amount, base, conditions] = output.split(':') + return new OutputDTO( + parseInt(amount), + parseInt(base), + conditions, + output + ) + }) + } + + getCompactVersion() { + let issuers = this.issuers; + let raw = ["TX", this.version, issuers.length, this.inputs.length, this.unlocks.length, this.outputs.length, this.comment ? 1 : 0, this.locktime || 0].join(':') + '\n'; + raw += this.blockstamp + "\n"; + (issuers || []).forEach((issuer) => { + raw += issuer + '\n'; + }); + (this.inputs || []).forEach((input) => { + raw += input + '\n'; + }); + (this.unlocks || []).forEach((input) => { + raw += input + '\n'; + }); + (this.outputs || []).forEach((output) => { + raw += output + '\n'; + }); + if (this.comment) + raw += this.comment + '\n'; + (this.signatures || []).forEach((signature) => { + raw += signature + '\n' + }) + return raw + } + + static fromJSONObject(obj:any) { + return new TransactionDTO( + obj.version, + obj.currency, + obj.locktime, + obj.hash, + obj.blockstamp, + obj.issuers, + obj.inputs, + obj.outputs, + obj.unlocks, + obj.signatures, + obj.comment + ) + } + + static toRAW(json:TransactionDTO, noSig = false) { + let raw = "" + raw += "Version: " + (json.version) + "\n" + raw += "Type: Transaction\n" + raw += "Currency: " + json.currency + "\n" + raw += "Blockstamp: " + json.blockstamp + "\n" + raw += "Locktime: " + json.locktime + "\n" + raw += "Issuers:\n"; + (json.issuers || []).forEach((issuer) => { + raw += issuer + '\n' + }) + raw += "Inputs:\n"; + (json.inputs || []).forEach((input) => { + raw += input + '\n' + }) + raw += "Unlocks:\n"; + (json.unlocks || []).forEach((unlock) => { + raw += unlock + '\n' + }) + raw += "Outputs:\n"; + (json.outputs || []).forEach((output) => { + raw += output + '\n' + }) + raw += "Comment: " + (json.comment || "") + "\n"; + if (!noSig) { + (json.signatures || []).forEach((signature) => { + raw += signature + '\n' + }) + } + return raw + } +} \ No newline at end of file diff --git a/app/lib/indexer.js b/app/lib/indexer.ts similarity index 51% rename from app/lib/indexer.js rename to app/lib/indexer.ts index eec10464ec7e142ce0711a5ba88036c81e912d98..bed96bc9bf78517d1dc892d06089848beab248a9 100644 --- a/app/lib/indexer.js +++ b/app/lib/indexer.ts @@ -1,4 +1,11 @@ "use strict"; +import {BlockDTO} from "./dto/BlockDTO" +import {ConfDTO} from "./dto/ConfDTO" +import {IdentityDTO} from "./dto/IdentityDTO"; +import {RevocationDTO} from "./dto/RevocationDTO" +import {CertificationDTO} from "./dto/CertificationDTO" +import {OutputDTO, TransactionDTO} from "./dto/TransactionDTO" +import {DBHead} from "./db/DBHead" const co = require('co'); const _ = require('underscore'); @@ -9,14 +16,119 @@ const rawer = common.rawer const unlock = common.txunlock const keyring = common.keyring const Block = common.document.Block -const Identity = common.document.Identity -const Certification = common.document.Certification const Membership = common.document.Membership -const Transaction = common.document.Transaction -const indexer = module.exports = { +export interface IndexEntry { + index: string, + op: string, + writtenOn: number, + written_on: string, +} + +export interface MindexEntry extends IndexEntry { + pub: string, + created_on: string, + type: string | null, + expires_on: number | null, + expired_on: number | null, + revocation: string | null, + revokes_on: number | null, + chainable_on: number | null, + revoked_on: string | null, + leaving: boolean | null, + age: number, + isBeingRevoked?: boolean, + unchainables: number, + numberFollowing?: boolean, + distanceOK?: boolean, + onRevoked?: boolean, + joinsTwice?: boolean, + enoughCerts?: boolean, + leaverIsMember?: boolean, + activeIsMember?: boolean, + revokedIsMember?: boolean, + alreadyRevoked?: boolean, + revocationSigOK?: boolean, +} + +export interface IindexEntry extends IndexEntry { + uid: string | null, + pub: string, + hash: string | null, + sig: string | null, + created_on: string | null, + member: boolean, + wasMember: boolean | null, + kick: boolean | null, + wid: number | null, + age: number, + pubUnique?: boolean, + excludedIsMember?: boolean, + isBeingKicked?: boolean, + uidUnique?: boolean, + hasToBeExcluded?: boolean, + wotb_id?: number, +} + +export interface CindexEntry extends IndexEntry { + issuer: string, + receiver: string, + created_on: number, + sig: string, + chainable_on: number, + expires_on: number, + expired_on: number, + from_wid: null, // <-These 2 fields are useless + to_wid: null, // <-' + unchainables: number, + age: number, + stock: number, + fromMember?: boolean, + toMember?: boolean, + toNewcomer?: boolean, + toLeaver?: boolean, + isReplay?: boolean, + sigOK?: boolean, +} + +export interface SindexEntry extends IndexEntry { + tx: string | null, + identifier: string, + pos: number, + created_on: string | null, + written_time: number, + locktime: number, + unlock: string | null, + amount: number, + base: number, + conditions: string, + consumed: boolean, + txObj: TransactionDTO, + age: number, + available?: boolean, + isLocked?: boolean, + isTimeLocked?: boolean, +} + +interface Ranger { + (n:number, m:number, prop?:string): Promise<DBHead[]> +} - localIndex: (block, conf) => { +function pushIindex(index: any[], entry: IindexEntry): void { + index.push(entry) +} + +function pushMindex(index: any[], entry: MindexEntry): void { + index.push(entry) +} + +function pushCindex(index: any[], entry: CindexEntry): void { + index.push(entry) +} + +export class Indexer { + + static localIndex(block:BlockDTO, conf:ConfDTO): IndexEntry[] { /******************** * GENERAL BEHAVIOR @@ -34,15 +146,15 @@ const indexer = module.exports = { * * for each tx input: 1 index (1 sindex UPDATE) */ - const index = []; + const index: IndexEntry[] = []; /*************************** * IDENTITIES INDEX (IINDEX) **************************/ for (const identity of block.identities) { - const idty = Identity.fromInline(identity); + const idty = IdentityDTO.fromInline(identity); // Computes the hash if not done yet - index.push({ + pushIindex(index, { index: constants.I_INDEX, op: constants.IDX_CREATE, uid: idty.uid, @@ -52,11 +164,12 @@ const indexer = module.exports = { created_on: idty.buid, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, member: true, wasMember: true, kick: false, wid: null // wotb id - }); + }) } /**************************** @@ -65,118 +178,145 @@ const indexer = module.exports = { // Joiners (newcomer or join back) for (const inlineMS of block.joiners) { const ms = Membership.fromInline(inlineMS); - const matchesANewcomer = _.filter(index, (row) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0; + const matchesANewcomer = _.filter(index, (row: IindexEntry) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0; if (matchesANewcomer) { // Newcomer - index.push({ + pushMindex(index, { 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('-'), writtenOn: block.number, + age: 0, + unchainables: 0, type: 'JOIN', expires_on: conf.msValidity, + expired_on: null, revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, - chainable_on: parseInt(block.medianTime) + conf.msPeriod, + revocation: null, + chainable_on: block.medianTime + conf.msPeriod, revoked_on: null, leaving: false - }); + }) } else { // Join back - index.push({ + pushMindex(index, { 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('-'), writtenOn: block.number, + age: 0, + unchainables: 0, type: 'JOIN', expires_on: conf.msValidity, + expired_on: null, revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, - chainable_on: parseInt(block.medianTime) + conf.msPeriod, + revocation: null, + chainable_on: block.medianTime + conf.msPeriod, revoked_on: null, leaving: null - }); - index.push({ + }) + pushIindex(index, { index: constants.I_INDEX, op: constants.IDX_UPDATE, uid: null, pub: ms.issuer, + hash: null, + sig: null, created_on: null, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, member: true, wasMember: null, kick: null, wid: null - }); + }) } } // Actives for (const inlineMS of block.actives) { const ms = Membership.fromInline(inlineMS); // Renew - index.push({ + pushMindex(index, { 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('-'), writtenOn: block.number, + age: 0, + unchainables: 0, type: 'ACTIVE', expires_on: conf.msValidity, + expired_on: null, revokes_on: conf.msValidity * constants.REVOCATION_FACTOR, - chainable_on: parseInt(block.medianTime) + conf.msPeriod, + revocation: null, + chainable_on: block.medianTime + conf.msPeriod, revoked_on: null, leaving: null - }); + }) } // Leavers for (const inlineMS of block.leavers) { const ms = Membership.fromInline(inlineMS); - index.push({ + pushMindex(index, { 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('-'), writtenOn: block.number, + age: 0, + unchainables: 0, type: 'LEAVE', expires_on: null, + expired_on: null, revokes_on: null, - chainable_on: parseInt(block.medianTime) + conf.msPeriod, + revocation: null, + chainable_on: block.medianTime + conf.msPeriod, revoked_on: null, leaving: true - }); + }) } // Revoked for (const inlineRevocation of block.revoked) { - const revocation = Identity.revocationFromInline(inlineRevocation); - index.push({ + const revocation = RevocationDTO.fromInline(inlineRevocation) + pushMindex(index, { 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('-'), writtenOn: block.number, + age: 0, + unchainables: 0, + type: null, expires_on: null, + expired_on: null, revokes_on: null, - revoked_on: [block.number, block.hash].join('-'), revocation: revocation.sig, + chainable_on: null, + revoked_on: [block.number, block.hash].join('-'), leaving: false - }); + }) } // Excluded for (const excluded of block.excluded) { - index.push({ + pushIindex(index, { index: constants.I_INDEX, op: constants.IDX_UPDATE, uid: null, pub: excluded, + hash: null, + sig: null, created_on: null, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, member: false, wasMember: null, kick: false, @@ -188,8 +328,8 @@ const indexer = module.exports = { * CERTIFICATIONS INDEX (CINDEX) ******************************/ for (const inlineCert of block.certifications) { - const cert = Certification.fromInline(inlineCert); - index.push({ + const cert = CertificationDTO.fromInline(inlineCert); + pushCindex(index, { index: constants.C_INDEX, op: constants.IDX_CREATE, issuer: cert.pubkey, @@ -197,129 +337,119 @@ const indexer = module.exports = { created_on: cert.block_number, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, + stock: conf.sigStock, + unchainables: 0, sig: cert.sig, - chainable_on: parseInt(block.medianTime) + conf.sigPeriod, + chainable_on: block.medianTime + conf.sigPeriod, expires_on: conf.sigValidity, expired_on: 0, from_wid: null, to_wid: null - }); + }) } - return index.concat(module.exports.localSIndex(block)); - }, + return index.concat(Indexer.localSIndex(block)); + } - localSIndex: (block) => { + static localSIndex(block:BlockDTO): SindexEntry[] { /******************************* * 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.currency; - const txObj = Transaction.fromJSON(obj); - const txHash = txObj.getHash(true); + const index: SindexEntry[] = []; + for (const tx of block.transactions) { + tx.currency = block.currency || tx.currency; + const txHash = tx.getHash() let k = 0; - for (const input of txObj.inputs) { + for (const input of tx.inputsAsObjects()) { index.push({ index: constants.S_INDEX, op: constants.IDX_UPDATE, tx: txHash, identifier: input.identifier, pos: input.pos, - created_on: obj.blockstamp, + created_on: tx.blockstamp, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, written_time: block.medianTime, - locktime: obj.locktime, - unlock: txObj.unlocks[k], + locktime: tx.locktime, + unlock: tx.unlocks[k], amount: input.amount, base: input.base, - conditions: null, + conditions: "", // Is overriden thereafter consumed: true, - txObj: txObj + txObj: tx }); k++; } let i = 0; - for (const output of txObj.outputs) { + for (const output of tx.outputsAsObjects()) { index.push({ index: constants.S_INDEX, op: constants.IDX_CREATE, tx: txHash, identifier: txHash, pos: i++, + created_on: null, written_on: [block.number, block.hash].join('-'), writtenOn: block.number, + age: 0, written_time: block.medianTime, - locktime: obj.locktime, + locktime: tx.locktime, + unlock: null, amount: output.amount, base: output.base, conditions: output.conditions, consumed: false, - txObj: obj + txObj: tx }); } } return index; - }, + } - quickCompleteGlobalScope: (block, conf, bindex, iindex, mindex, cindex, dal) => co(function*() { + static async quickCompleteGlobalScope(block: BlockDTO, conf: ConfDTO, bindex: DBHead[], iindex: IindexEntry[], mindex: MindexEntry[], cindex: CindexEntry[], dal: any) { - function range(start, end, property) { - return co(function*() { - let theRange; - end = Math.min(end, bindex.length); - if (start == 1) { - theRange = bindex.slice(-end); - } else { - theRange = bindex.slice(-end, -start + 1); - } - theRange.reverse(); - if (property) { - // Filter on a particular property - return theRange.map((b) => b[property]); - } else { - return theRange; - } - }); + function range(start: number, end: number, property = ""): any { + let theRange; + end = Math.min(end, bindex.length); + if (start == 1) { + theRange = bindex.slice(-end); + } else { + theRange = bindex.slice(-end, -start + 1); + } + theRange.reverse(); + if (property) { + // Filter on a particular property + return theRange.map((b:any) => b[property]); + } else { + return theRange; + } } - function head(n) { - return co(function*() { - return (yield range(n, n))[0]; - }); + async function head(n:number) { + return range(n, n)[0]; } - const HEAD = { - version: block.version, - currency: block.currency, - bsize: Block.getLen(block), - hash: Block.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); + const HEAD = new DBHead() + + HEAD.version = block.version + HEAD.currency = block.currency + HEAD.bsize = Block.getLen(block) + HEAD.hash = Block.getHash(block) + HEAD.issuer = block.issuer + HEAD.time = block.time + HEAD.medianTime = block.medianTime + HEAD.number = block.number + HEAD.powMin = block.powMin + HEAD.unitBase = block.unitbase + HEAD.membersCount = block.membersCount + HEAD.dividend = block.dividend + HEAD.new_dividend = null + + const HEAD_1 = await head(1); if (HEAD.number == 0) { HEAD.dividend = conf.ud0; @@ -331,72 +461,73 @@ const indexer = module.exports = { } // BR_G04 - yield indexer.prepareIssuersCount(HEAD, range, HEAD_1); + await Indexer.prepareIssuersCount(HEAD, range, HEAD_1); // BR_G05 - indexer.prepareIssuersFrame(HEAD, HEAD_1); + Indexer.prepareIssuersFrame(HEAD, HEAD_1); // BR_G06 - indexer.prepareIssuersFrameVar(HEAD, HEAD_1); + Indexer.prepareIssuersFrameVar(HEAD, HEAD_1); // BR_G07 - yield indexer.prepareAvgBlockSize(HEAD, range); + await Indexer.prepareAvgBlockSize(HEAD, range); // BR_G09 - indexer.prepareDiffNumber(HEAD, HEAD_1, conf); + Indexer.prepareDiffNumber(HEAD, HEAD_1, conf) // BR_G11 - indexer.prepareUDTime(HEAD, HEAD_1, conf); + Indexer.prepareUDTime(HEAD, HEAD_1, conf) // BR_G15 - indexer.prepareMass(HEAD, HEAD_1); + Indexer.prepareMass(HEAD, HEAD_1); // BR_G16 - yield indexer.prepareSpeed(HEAD, head, conf); + await Indexer.prepareSpeed(HEAD, head, conf) // BR_G19 - yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); + await Indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); // BR_G22 - yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); + await Indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); // BR_G37 - yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal); + await Indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal); // BR_G104 - yield indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, dal); + await Indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, dal); // BR_G105 - yield indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, dal); + await Indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, dal); return HEAD; - }), + } - completeGlobalScope: (block, conf, index, dal) => co(function*() { + static async completeGlobalScope(block: BlockDTO, conf: ConfDTO, index: IndexEntry[], dal: any) { - 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 iindex = Indexer.iindex(index); + const mindex = Indexer.mindex(index); + const cindex = Indexer.cindex(index); + const sindex = Indexer.sindex(index); const range = dal.range; const head = dal.head; - const HEAD = { - version: block.version, - bsize: Block.getLen(block), - hash: Block.getHash(block), - issuer: block.issuer, - time: block.time, - powMin: block.powMin - }; - const HEAD_1 = yield head(1); + const HEAD = new DBHead() + + HEAD.version = block.version + HEAD.bsize = Block.getLen(block) + HEAD.hash = Block.getHash(block) + HEAD.issuer = block.issuer + HEAD.time = block.time + HEAD.powMin = block.powMin + + const HEAD_1 = await head(1); if (HEAD_1) { HEAD_1.currency = conf.currency; } // BR_G01 - indexer.prepareNumber(HEAD, HEAD_1); + Indexer.prepareNumber(HEAD, HEAD_1); // BR_G02 if (HEAD.number > 0) { @@ -421,44 +552,44 @@ const indexer = module.exports = { // BR_G03 if (HEAD.number > 0) { - HEAD.issuerIsMember = reduce(yield dal.iindexDAL.reducable(HEAD.issuer)).member; + HEAD.issuerIsMember = reduce(await 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); + await Indexer.prepareIssuersCount(HEAD, range, HEAD_1); // BR_G05 - indexer.prepareIssuersFrame(HEAD, HEAD_1); + Indexer.prepareIssuersFrame(HEAD, HEAD_1); // BR_G06 - indexer.prepareIssuersFrameVar(HEAD, HEAD_1); + Indexer.prepareIssuersFrameVar(HEAD, HEAD_1); // BR_G07 - yield indexer.prepareAvgBlockSize(HEAD, range); + await Indexer.prepareAvgBlockSize(HEAD, range); // BR_G08 if (HEAD.number > 0) { - HEAD.medianTime = Math.max(HEAD_1.medianTime, average(yield range(1, Math.min(conf.medianTimeBlocks, HEAD.number), 'time'))); + HEAD.medianTime = Math.max(HEAD_1.medianTime, average(await range(1, Math.min(conf.medianTimeBlocks, HEAD.number), 'time'))); } else { HEAD.medianTime = HEAD.time; } // BR_G09 - indexer.prepareDiffNumber(HEAD, HEAD_1, conf); + Indexer.prepareDiffNumber(HEAD, HEAD_1, conf) // BR_G10 if (HEAD.number == 0) { - HEAD.membersCount = count(_.filter(iindex, (entry) => entry.member === true)); + HEAD.membersCount = count(_.filter(iindex, (entry:IindexEntry) => entry.member === true)); } else { HEAD.membersCount = HEAD_1.membersCount - + count(_.filter(iindex, (entry) => entry.member === true)) - - count(_.filter(iindex, (entry) => entry.member === false)); + + count(_.filter(iindex, (entry:IindexEntry) => entry.member === true)) + - count(_.filter(iindex, (entry:IindexEntry) => entry.member === false)); } // BR_G11 - indexer.prepareUDTime(HEAD, HEAD_1, conf); + Indexer.prepareUDTime(HEAD, HEAD_1, conf) // BR_G12 if (HEAD.number == 0) { @@ -468,16 +599,16 @@ const indexer = module.exports = { } // BR_G13 - indexer.prepareDividend(HEAD, HEAD_1, conf); + Indexer.prepareDividend(HEAD, HEAD_1, conf) // BR_G14 - indexer.prepareUnitBase(HEAD, HEAD_1, conf); + Indexer.prepareUnitBase(HEAD); // BR_G15 - indexer.prepareMass(HEAD, HEAD_1); + Indexer.prepareMass(HEAD, HEAD_1); // BR_G16 - yield indexer.prepareSpeed(HEAD, head, conf); + await Indexer.prepareSpeed(HEAD, head, conf) // BR_G17 if (HEAD.number > 0) { @@ -502,75 +633,73 @@ const indexer = module.exports = { } // BR_G18 - yield indexer.preparePersonalizedPoW(HEAD, HEAD_1, range, conf); + await Indexer.preparePersonalizedPoW(HEAD, HEAD_1, range, conf) // BR_G19 - yield indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); + await Indexer.prepareIdentitiesAge(iindex, HEAD, HEAD_1, conf, dal); // BR_G20 - yield iindex.map((ENTRY) => co(function*() { + await Promise.all(iindex.map(async (ENTRY: IindexEntry) => { if (ENTRY.op == constants.IDX_CREATE) { - ENTRY.uidUnique = count(yield dal.iindexDAL.sqlFind({ uid: ENTRY.uid })) == 0; + ENTRY.uidUnique = count(await dal.iindexDAL.sqlFind({ uid: ENTRY.uid })) == 0; } else { ENTRY.uidUnique = true; } - })); + })) // BR_G21 - yield iindex.map((ENTRY) => co(function*() { + await Promise.all(iindex.map(async (ENTRY: IindexEntry) => { if (ENTRY.op == constants.IDX_CREATE) { - ENTRY.pubUnique = count(yield dal.iindexDAL.sqlFind({pub: ENTRY.pub})) == 0; + ENTRY.pubUnique = count(await dal.iindexDAL.sqlFind({pub: ENTRY.pub})) == 0; } else { ENTRY.pubUnique = true; } - })); + })) // BR_G33 - yield iindex.map((ENTRY) => co(function*() { + await Promise.all(iindex.map(async (ENTRY: IindexEntry) => { if (ENTRY.member !== false) { ENTRY.excludedIsMember = true; } else { - ENTRY.excludedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + ENTRY.excludedIsMember = reduce(await dal.iindexDAL.reducable(ENTRY.pub)).member; } - })); + })) // BR_G34 - yield mindex.map((ENTRY) => co(function*() { + mindex.map((ENTRY: MindexEntry) => { ENTRY.isBeingRevoked = !!ENTRY.revoked_on; - })); + }) // BR_G107 if (HEAD.number > 0) { - yield mindex.map((ENTRY) => co(function*() { - const rows = yield dal.mindexDAL.sqlFind({ pub: ENTRY.pub, chainable_on: { $gt: HEAD_1.medianTime }}); + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { + const rows = await dal.mindexDAL.sqlFind({ pub: ENTRY.pub, chainable_on: { $gt: HEAD_1.medianTime }}); // This rule will be enabled on if (HEAD.medianTime >= 1498860000) { ENTRY.unchainables = count(rows); - } else { - ENTRY.unchainables = [] } - })); + })) } // BR_G35 - yield iindex.map((ENTRY) => co(function*() { - ENTRY.isBeingKicked = ENTRY.member === false; - })); + await Promise.all(iindex.map(async (ENTRY: IindexEntry) => { + ENTRY.isBeingKicked = ENTRY.member === false + })) // BR_G36 - yield iindex.map((ENTRY) => co(function*() { - const isMarkedAsToKick = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).kick; - const isBeingRevoked = count(_.filter(mindex, (m) => m.isBeingRevoked && m.pub == ENTRY.pub)) == 1; + await Promise.all(iindex.map(async (ENTRY: IindexEntry) => { + const isMarkedAsToKick = reduce(await dal.iindexDAL.reducable(ENTRY.pub)).kick; + const isBeingRevoked = count(_.filter(mindex, (m:MindexEntry) => m.isBeingRevoked && m.pub == ENTRY.pub)) == 1; ENTRY.hasToBeExcluded = isMarkedAsToKick || isBeingRevoked; - })); + })) // BR_G22 - yield indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); + await Indexer.prepareMembershipsAge(mindex, HEAD, HEAD_1, conf, dal); // BR_G23 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (!ENTRY.revoked_on) { - const created_on = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).created_on; + const created_on = reduce(await dal.mindexDAL.reducable(ENTRY.pub)).created_on; if (created_on != null) { ENTRY.numberFollowing = number(ENTRY.created_on) > number(created_on); } else { @@ -579,23 +708,23 @@ const indexer = module.exports = { } else { ENTRY.numberFollowing = true; } - })); + })) // BR_G24 // Global testing, because of wotb - const oneIsOutdistanced = yield checkPeopleAreNotOudistanced( - _.filter(mindex, (entry) => !entry.revoked_on).map((entry) => entry.pub), - cindex.reduce((newLinks, c) => { + const oneIsOutdistanced = await checkPeopleAreNotOudistanced( + _.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map((entry: MindexEntry) => entry.pub), + cindex.reduce((newLinks:any, c: CindexEntry) => { 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), + _.where(iindex, { op: constants.IDX_CREATE }).map((entry: IindexEntry) => entry.pub), conf, dal ); - mindex.map((ENTRY) => { + mindex.map((ENTRY: MindexEntry) => { if (ENTRY.expires_on) { ENTRY.distanceOK = !oneIsOutdistanced; } else { @@ -604,142 +733,142 @@ const indexer = module.exports = { }); // BR_G25 - yield mindex.map((ENTRY) => co(function*() { - ENTRY.onRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on != null; - })); + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { + ENTRY.onRevoked = reduce(await 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; - })); + await Promise.all(_.filter(mindex, (entry: MindexEntry) => entry.op == constants.IDX_UPDATE && entry.expired_on === 0).map(async (ENTRY: MindexEntry) => { + ENTRY.joinsTwice = reduce(await dal.iindexDAL.reducable(ENTRY.pub)).member == true; + })) // BR_G27 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { 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)); + const existing = count(await dal.cindexDAL.sqlFind({ receiver: ENTRY.pub, expired_on: 0 })) + const pending = count(_.filter(cindex, (c:CindexEntry) => 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*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (ENTRY.type == 'LEAVE') { - ENTRY.leaverIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + ENTRY.leaverIsMember = reduce(await dal.iindexDAL.reducable(ENTRY.pub)).member } else { ENTRY.leaverIsMember = true; } - })); + })) // BR_G29 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (ENTRY.type == 'ACTIVE') { - const reducable = yield dal.iindexDAL.reducable(ENTRY.pub); + const reducable = await dal.iindexDAL.reducable(ENTRY.pub) ENTRY.activeIsMember = reduce(reducable).member; } else { ENTRY.activeIsMember = true; } - })); + })) // BR_G30 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (!ENTRY.revoked_on) { ENTRY.revokedIsMember = true; } else { - ENTRY.revokedIsMember = reduce(yield dal.iindexDAL.reducable(ENTRY.pub)).member; + ENTRY.revokedIsMember = reduce(await dal.iindexDAL.reducable(ENTRY.pub)).member } - })); + })) // BR_G31 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (!ENTRY.revoked_on) { ENTRY.alreadyRevoked = false; } else { - ENTRY.alreadyRevoked = reduce(yield dal.mindexDAL.reducable(ENTRY.pub)).revoked_on; + ENTRY.alreadyRevoked = reduce(await dal.mindexDAL.reducable(ENTRY.pub)).revoked_on } - })); + })) // BR_G32 - yield mindex.map((ENTRY) => co(function*() { + await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (!ENTRY.revoked_on) { ENTRY.revocationSigOK = true; } else { - ENTRY.revocationSigOK = yield sigCheckRevoke(ENTRY, dal, block.currency); + ENTRY.revocationSigOK = await sigCheckRevoke(ENTRY, dal, block.currency); } - })); + })) // BR_G37 - yield indexer.prepareCertificationsAge(cindex, HEAD, HEAD_1, conf, dal); + await 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 }}); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + const rows = await 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.getValidLinksFrom(ENTRY.issuer)); - })); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + ENTRY.stock = count(await dal.cindexDAL.getValidLinksFrom(ENTRY.issuer)) + })) // BR_G40 - yield cindex.map((ENTRY) => co(function*() { - ENTRY.fromMember = reduce(yield dal.iindexDAL.reducable(ENTRY.issuer)).member; - })); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + ENTRY.fromMember = reduce(await dal.iindexDAL.reducable(ENTRY.issuer)).member + })) // BR_G41 - yield cindex.map((ENTRY) => co(function*() { - ENTRY.toMember = reduce(yield dal.iindexDAL.reducable(ENTRY.receiver)).member; - })); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + ENTRY.toMember = reduce(await dal.iindexDAL.reducable(ENTRY.receiver)).member + })) // BR_G42 - yield cindex.map((ENTRY) => co(function*() { + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { 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; - })); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + ENTRY.toLeaver = reduce(await dal.mindexDAL.reducable(ENTRY.receiver)).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; - })); + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + const reducable = await 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) => { + await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { + ENTRY.sigOK = await checkCertificationIsValid(block, ENTRY, async (block:BlockDTO,pub:string,dal:any) => { let localInlineIdty = block.getInlineIdentity(pub); if (localInlineIdty) { - return Identity.fromInline(localInlineIdty); + return IdentityDTO.fromInline(localInlineIdty) } - return dal.getWrittenIdtyByPubkey(pub); + return dal.getWrittenIdtyByPubkey(pub) }, conf, dal); - })); + })) // BR_G102 - yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { + await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { - let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + let ref = await 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.TX_WINDOW + 1; } } - })); + })) // BR_G46 - yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { - const reducable = yield dal.sindexDAL.sqlFind({ + await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + const reducable = await dal.sindexDAL.sqlFind({ identifier: ENTRY.identifier, pos: ENTRY.pos, amount: ENTRY.amount, @@ -747,13 +876,13 @@ const indexer = module.exports = { }); ENTRY.conditions = reduce(reducable).conditions; // We valuate the input conditions, so we can map these records to a same account ENTRY.available = reduce(reducable).consumed === false; - })); + })) // BR_G47 - yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { - let source = _.filter(sindex, (src) => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos && src.conditions && src.op === constants.IDX_CREATE)[0]; + await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + let source = _.filter(sindex, (src:SindexEntry) => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos && src.conditions && src.op === constants.IDX_CREATE)[0]; if (!source) { - const reducable = yield dal.sindexDAL.sqlFind({ + const reducable = await dal.sindexDAL.sqlFind({ identifier: ENTRY.identifier, pos: ENTRY.pos, amount: ENTRY.amount, @@ -763,41 +892,41 @@ const indexer = module.exports = { } ENTRY.conditions = source.conditions; ENTRY.isLocked = !txSourceUnlock(ENTRY, source, HEAD); - })); + })) // BR_G48 - yield _.where(sindex, { op: constants.IDX_UPDATE }).map((ENTRY) => co(function*() { - ENTRY.isTimeLocked = ENTRY.written_time - reduce(yield dal.sindexDAL.sqlFind({ + await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + ENTRY.isTimeLocked = ENTRY.written_time - reduce(await dal.sindexDAL.sqlFind({ identifier: ENTRY.identifier, pos: ENTRY.pos, amount: ENTRY.amount, base: ENTRY.base })).written_time < ENTRY.locktime; - })); + })) return HEAD; - }), + } // BR_G01 - prepareNumber: (HEAD, HEAD_1) => { + static prepareNumber(HEAD: DBHead, HEAD_1: DBHead) { if (HEAD_1) { HEAD.number = HEAD_1.number + 1; } else { HEAD.number = 0; } - }, + } // BR_G04 - prepareIssuersCount: (HEAD, range, HEAD_1) => co(function*() { + static async prepareIssuersCount(HEAD: DBHead, range:Ranger, HEAD_1: DBHead) { if (HEAD.number == 0) { HEAD.issuersCount = 0; } else { - HEAD.issuersCount = count(uniq(yield range(1, HEAD_1.issuersFrame, 'issuer'))); + HEAD.issuersCount = count(uniq(await range(1, HEAD_1.issuersFrame, 'issuer'))); // TODO } - }), + } // BR_G05 - prepareIssuersFrame: (HEAD, HEAD_1) => { + static prepareIssuersFrame(HEAD: DBHead, HEAD_1: DBHead) { if (HEAD.number == 0) { HEAD.issuersFrame = 1; } else if (HEAD_1.issuersFrameVar > 0) { @@ -807,10 +936,10 @@ const indexer = module.exports = { } else { HEAD.issuersFrame = HEAD_1.issuersFrame } - }, + } // BR_G06 - prepareIssuersFrameVar: (HEAD, HEAD_1) => { + static prepareIssuersFrameVar(HEAD: DBHead, HEAD_1: DBHead) { if (HEAD.number == 0) { HEAD.issuersFrameVar = 0; } else { @@ -823,15 +952,15 @@ const indexer = module.exports = { HEAD.issuersFrameVar = HEAD_1.issuersFrameVar + 5 * issuersVar; } } - }, + } // BR_G07 - prepareAvgBlockSize: (HEAD, range) => co(function*() { - HEAD.avgBlockSize = average(yield range(1, HEAD.issuersCount, 'bsize')); - }), + static async prepareAvgBlockSize(HEAD: DBHead, range: (n:number,m:number,s:string)=>Promise<number[]>) { + HEAD.avgBlockSize = average(await range(1, HEAD.issuersCount, 'bsize')); // TODO: vérifier l'appel asynchrone + } // BR_G09 - prepareDiffNumber: (HEAD, HEAD_1, conf) => { + static prepareDiffNumber(HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO) { if (HEAD.number == 0) { HEAD.diffNumber = HEAD.number + conf.dtDiffEval; } else if (HEAD_1.diffNumber <= HEAD.number) { @@ -839,10 +968,10 @@ const indexer = module.exports = { } else { HEAD.diffNumber = HEAD_1.diffNumber; } - }, + } // BR_G11 - prepareUDTime: (HEAD, HEAD_1, conf) => { + static prepareUDTime(HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO) { // UD Production if (HEAD.number == 0) { HEAD.udTime = conf.udTime0; @@ -859,10 +988,10 @@ const indexer = module.exports = { } else { HEAD.udReevalTime = HEAD_1.udReevalTime; } - }, + } // BR_G13 - prepareDividend: (HEAD, HEAD_1, conf) => { + static prepareDividend(HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO) { // UD re-evaluation if (HEAD.number == 0) { HEAD.dividend = conf.ud0; @@ -881,19 +1010,19 @@ const indexer = module.exports = { } else { HEAD.new_dividend = null; } - }, + } // BR_G14 - prepareUnitBase: (HEAD) => { + static prepareUnitBase(HEAD: DBHead) { if (HEAD.dividend >= Math.pow(10, constants.NB_DIGITS_UD)) { HEAD.dividend = Math.ceil(HEAD.dividend / 10); HEAD.new_dividend = HEAD.dividend; HEAD.unitBase = HEAD.unitBase + 1; } - }, + } // BR_G15 - prepareMass: (HEAD, HEAD_1) => { + static prepareMass(HEAD: DBHead, HEAD_1: DBHead) { // Mass if (HEAD.number == 0) { HEAD.mass = 0; @@ -912,36 +1041,36 @@ const indexer = module.exports = { } else { HEAD.massReeval = HEAD_1.massReeval; } - }, + } // BR_G16 - prepareSpeed: (HEAD, head, conf) => co(function*() { + static async prepareSpeed(HEAD: DBHead, head: (n:number) => Promise<BlockDTO>, conf: ConfDTO) { if (HEAD.number == 0) { HEAD.speed = 0; } else { const quantity = Math.min(conf.dtDiffEval, HEAD.number); - const elapsed = (HEAD.medianTime - (yield head(quantity)).medianTime); + const elapsed = (HEAD.medianTime - (await head(quantity)).medianTime); if (!elapsed) { HEAD.speed = 100; } else { HEAD.speed = quantity / elapsed; } } - }), + } // BR_G18 - preparePersonalizedPoW: (HEAD, HEAD_1, range, conf) => co(function*() { + static async preparePersonalizedPoW(HEAD: DBHead, HEAD_1: DBHead, range: (n:number,m:number)=>Promise<BlockDTO>, conf: ConfDTO) { let nbPersonalBlocksInFrame, medianOfBlocksInFrame, blocksOfIssuer; let nbPreviousIssuers = 0, nbBlocksSince = 0; if (HEAD.number == 0) { nbPersonalBlocksInFrame = 0; medianOfBlocksInFrame = 1; } else { - const blocksInFrame = _.filter(yield range(1, HEAD_1.issuersFrame), (b) => b.number <= HEAD_1.number); - const issuersInFrame = blocksInFrame.map((b) => b.issuer); - blocksOfIssuer = _.filter(blocksInFrame, (entry) => entry.issuer == HEAD.issuer); + const blocksInFrame = _.filter(await range(1, HEAD_1.issuersFrame), (b:BlockDTO) => b.number <= HEAD_1.number); + const issuersInFrame = blocksInFrame.map((b:BlockDTO) => b.issuer); + blocksOfIssuer = _.filter(blocksInFrame, (entry:BlockDTO) => entry.issuer == HEAD.issuer); nbPersonalBlocksInFrame = count(blocksOfIssuer); - const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer) => count(_.where(blocksInFrame, { issuer }))); + const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer:string) => count(_.where(blocksInFrame, { issuer }))); medianOfBlocksInFrame = Math.max(1, median(blocksPerIssuerInFrame)); if (nbPersonalBlocksInFrame == 0) { nbPreviousIssuers = 0; @@ -963,125 +1092,145 @@ const indexer = module.exports = { 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*() { + static async prepareIdentitiesAge(iindex: IindexEntry[], HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO, dal: any) { + await Promise.all(_.where(iindex, { op: constants.IDX_CREATE }).map(async (ENTRY: IindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { - let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + let ref = await 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*() { + static async prepareMembershipsAge(mindex: MindexEntry[], HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO, dal: any) { + await Promise.all(_.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map(async (ENTRY:MindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { - let ref = yield dal.getBlockByBlockstamp(ENTRY.created_on); + let ref = await 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*() { + static async prepareCertificationsAge(cindex: CindexEntry[], HEAD: DBHead, HEAD_1: DBHead, conf: ConfDTO, dal: any) { + await Promise.all(cindex.map(async (ENTRY) => { if (HEAD.number == 0) { ENTRY.age = 0; } else { - let ref = yield dal.getBlock(number(ENTRY.created_on)); + let ref = await dal.getBlock(ENTRY.created_on) if (ref) { ENTRY.age = HEAD_1.medianTime - ref.medianTime; } else { ENTRY.age = conf.sigWindow + 1; } } - })); - }), + })) + } // BR_G49 - ruleVersion: (HEAD, HEAD_1) => { + static ruleVersion(HEAD: DBHead, HEAD_1: DBHead) { if (HEAD.number > 0) { return HEAD.version == HEAD_1.version || HEAD.version == HEAD_1.version + 1; } return true; - }, + } // BR_G50 - ruleBlockSize: (HEAD) => { + static ruleBlockSize(HEAD: DBHead) { if (HEAD.number > 0) { - return HEAD.bsize < indexer.DUP_HELPERS.getMaxBlockSize(HEAD); + return HEAD.bsize < Indexer.DUP_HELPERS.getMaxBlockSize(HEAD); } return true; - }, + } // BR_G98 - ruleCurrency: (block, HEAD) => { + static ruleCurrency(block:BlockDTO, HEAD: DBHead) { if (HEAD.number > 0) { return block.currency === HEAD.currency; } return true; - }, + } // BR_G51 - ruleNumber: (block, HEAD) => block.number == HEAD.number, + static ruleNumber(block:BlockDTO, HEAD: DBHead) { + return block.number == HEAD.number + } // BR_G52 - rulePreviousHash: (block, HEAD) => { - const equal = block.previousHash == HEAD.previousHash || (!block.previousHash && !HEAD.previousHash) - return equal - }, + static rulePreviousHash(block:BlockDTO, HEAD: DBHead) { + return block.previousHash == HEAD.previousHash || (!block.previousHash && !HEAD.previousHash) + } // BR_G53 - rulePreviousIssuer: (block, HEAD) => block.previousIssuer == HEAD.previousIssuer || (!block.previousIssuer && !HEAD.previousIssuer), + static rulePreviousIssuer(block:BlockDTO, HEAD: DBHead) { + return block.previousIssuer == HEAD.previousIssuer || (!block.previousIssuer && !HEAD.previousIssuer) + } // BR_G101 - ruleIssuerIsMember: (HEAD) => HEAD.issuerIsMember == true, + static ruleIssuerIsMember(HEAD: DBHead) { + return HEAD.issuerIsMember == true + } // BR_G54 - ruleIssuersCount: (block, HEAD) => block.issuersCount == HEAD.issuersCount, + static ruleIssuersCount(block:BlockDTO, HEAD: DBHead) { + return block.issuersCount == HEAD.issuersCount + } // BR_G55 - ruleIssuersFrame: (block, HEAD) => block.issuersFrame == HEAD.issuersFrame, + static ruleIssuersFrame(block:BlockDTO, HEAD: DBHead) { + return block.issuersFrame == HEAD.issuersFrame + } // BR_G56 - ruleIssuersFrameVar: (block, HEAD) => block.issuersFrameVar == HEAD.issuersFrameVar, + static ruleIssuersFrameVar(block:BlockDTO, HEAD: DBHead) { + return block.issuersFrameVar == HEAD.issuersFrameVar + } // BR_G57 - ruleMedianTime: (block, HEAD) => block.medianTime == HEAD.medianTime, + static ruleMedianTime(block:BlockDTO, HEAD: DBHead) { + return block.medianTime == HEAD.medianTime + } // BR_G58 - ruleDividend: (block, HEAD) => block.dividend == HEAD.new_dividend, + static ruleDividend(block:BlockDTO, HEAD: DBHead) { + return block.dividend == HEAD.new_dividend + } // BR_G59 - ruleUnitBase: (block, HEAD) => block.unitbase == HEAD.unitBase, + static ruleUnitBase(block:BlockDTO, HEAD: DBHead) { + return block.unitbase == HEAD.unitBase + } // BR_G60 - ruleMembersCount: (block, HEAD) => block.membersCount == HEAD.membersCount, + static ruleMembersCount(block:BlockDTO, HEAD: DBHead) { + return block.membersCount == HEAD.membersCount + } // BR_G61 - rulePowMin: (block, HEAD) => { + static rulePowMin(block: BlockDTO, HEAD: DBHead) { if (HEAD.number > 0) { return block.powMin == HEAD.powMin; } - }, + return true + } // BR_G62 - ruleProofOfWork: (HEAD) => { + static ruleProofOfWork(HEAD: DBHead) { // Compute exactly how much zeros are required for this block's issuer const remainder = HEAD.powRemainder; const nbZerosReq = HEAD.powZeros; @@ -1089,7 +1238,8 @@ const indexer = module.exports = { 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 match = HEAD.hash.match(/^0*/) + const givenZeros = Math.max(0, Math.min(nbZerosReq, (match && match[0].length) || 0)) 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 + ']'); } @@ -1098,110 +1248,124 @@ const indexer = module.exports = { console.error(e) return false; } - }, + } // BR_G63 - ruleIdentityWritability: (iindex, conf) => { + static ruleIdentityWritability(iindex: IindexEntry[], conf: ConfDTO) { for (const ENTRY of iindex) { if (ENTRY.age > conf.idtyWindow) return false; } - }, + return true + } // BR_G64 - ruleMembershipWritability: (mindex, conf) => { + static ruleMembershipWritability(mindex: MindexEntry[], conf: ConfDTO) { for (const ENTRY of mindex) { if (ENTRY.age > conf.msWindow) return false; } - }, + return true + } // BR_G108 - ruleMembershipPeriod: (mindex) => { + static ruleMembershipPeriod(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (ENTRY.unchainables > 0) return false; } - }, + return true + } // BR_G65 - ruleCertificationWritability: (cindex, conf) => { + static ruleCertificationWritability(cindex: CindexEntry[], conf: ConfDTO) { for (const ENTRY of cindex) { if (ENTRY.age > conf.sigWindow) return false; } - }, + return true + } // BR_G66 - ruleCertificationStock: (cindex, conf) => { + static ruleCertificationStock(cindex: CindexEntry[], conf: ConfDTO) { for (const ENTRY of cindex) { if (ENTRY.stock > conf.sigStock) return false; } - }, + return true + } // BR_G67 - ruleCertificationPeriod: (cindex) => { + static ruleCertificationPeriod(cindex: CindexEntry[]) { for (const ENTRY of cindex) { if (ENTRY.unchainables > 0) return false; } - }, + return true + } // BR_G68 - ruleCertificationFromMember: (HEAD, cindex) => { + static ruleCertificationFromMember(HEAD: DBHead, cindex: CindexEntry[]) { if (HEAD.number > 0) { for (const ENTRY of cindex) { if (!ENTRY.fromMember) return false; } } - }, + return true + } // BR_G69 - ruleCertificationToMemberOrNewcomer: (cindex) => { + static ruleCertificationToMemberOrNewcomer(cindex: CindexEntry[]) { for (const ENTRY of cindex) { if (!ENTRY.toMember && !ENTRY.toNewcomer) return false; } - }, + return true + } // BR_G70 - ruleCertificationToLeaver: (cindex) => { + static ruleCertificationToLeaver(cindex: CindexEntry[]) { for (const ENTRY of cindex) { if (ENTRY.toLeaver) return false; } - }, + return true + } // BR_G71 - ruleCertificationReplay: (cindex) => { + static ruleCertificationReplay(cindex: CindexEntry[]) { for (const ENTRY of cindex) { if (ENTRY.isReplay) return false; } - }, + return true + } // BR_G72 - ruleCertificationSignature: (cindex) => { + static ruleCertificationSignature(cindex: CindexEntry[]) { for (const ENTRY of cindex) { if (!ENTRY.sigOK) return false; } - }, + return true + } // BR_G73 - ruleIdentityUIDUnicity: (iindex) => { + static ruleIdentityUIDUnicity(iindex: IindexEntry[]) { for (const ENTRY of iindex) { if (!ENTRY.uidUnique) return false; } - }, + return true + } // BR_G74 - ruleIdentityPubkeyUnicity: (iindex) => { + static ruleIdentityPubkeyUnicity(iindex: IindexEntry[]) { for (const ENTRY of iindex) { if (!ENTRY.pubUnique) return false; } - }, + return true + } // BR_G75 - ruleMembershipSuccession: (mindex) => { + static ruleMembershipSuccession(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.numberFollowing) return false; } - }, + return true + } // BR_G76 - ruleMembershipDistance: (HEAD, mindex) => { + static ruleMembershipDistance(HEAD: DBHead, mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (HEAD.currency == 'gtest' && !ENTRY.distanceOK @@ -1218,139 +1382,155 @@ const indexer = module.exports = { return false; } } - }, + return true + } // BR_G77 - ruleMembershipOnRevoked: (mindex) => { + static ruleMembershipOnRevoked(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (ENTRY.onRevoked) return false; } - }, + return true + } // BR_G78 - ruleMembershipJoinsTwice: (mindex) => { + static ruleMembershipJoinsTwice(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (ENTRY.joinsTwice) return false; } - }, + return true + } // BR_G79 - ruleMembershipEnoughCerts: (mindex) => { + static ruleMembershipEnoughCerts(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.enoughCerts) return false; } - }, + return true + } // BR_G80 - ruleMembershipLeaverIsMember: (mindex) => { + static ruleMembershipLeaverIsMember(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.leaverIsMember) return false; } - }, + return true + } // BR_G81 - ruleMembershipActiveIsMember: (mindex) => { + static ruleMembershipActiveIsMember(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.activeIsMember) return false; } - }, + return true + } // BR_G82 - ruleMembershipRevokedIsMember: (mindex) => { + static ruleMembershipRevokedIsMember(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.revokedIsMember) return false; } - }, + return true + } // BR_G83 - ruleMembershipRevokedSingleton: (mindex) => { + static ruleMembershipRevokedSingleton(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (ENTRY.alreadyRevoked) return false; } - }, + return true + } // BR_G84 - ruleMembershipRevocationSignature: (mindex) => { + static ruleMembershipRevocationSignature(mindex: MindexEntry[]) { for (const ENTRY of mindex) { if (!ENTRY.revocationSigOK) return false; } - }, + return true + } // BR_G85 - ruleMembershipExcludedIsMember: (iindex) => { + static ruleMembershipExcludedIsMember(iindex: IindexEntry[]) { for (const ENTRY of iindex) { if (!ENTRY.excludedIsMember) return false; } - }, + return true + } // BR_G86 - ruleToBeKickedArePresent: (iindex, dal) => co(function*() { - const toBeKicked = yield dal.iindexDAL.getToBeKickedPubkeys(); + static async ruleToBeKickedArePresent(iindex: IindexEntry[], dal:any) { + const toBeKicked = await dal.iindexDAL.getToBeKickedPubkeys(); for (const toKick of toBeKicked) { if (count(_.where(iindex, { pub: toKick, isBeingKicked: true })) !== 1) { return false; } } - const beingKicked = _.filter(iindex, (i) => i.member === false); + const beingKicked = _.filter(iindex, (i:IindexEntry) => i.member === false); for (const entry of beingKicked) { if (!entry.hasToBeExcluded) { return false; } } - }), + return true + } // BR_G103 - ruleTxWritability: (sindex) => { + static ruleTxWritability(sindex: SindexEntry[]) { for (const ENTRY of sindex) { if (ENTRY.age > constants.TX_WINDOW) return false; } - }, + return true + } // BR_G87 - ruleInputIsAvailable: (sindex) => { + static ruleInputIsAvailable(sindex: SindexEntry[]) { const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (!ENTRY.available) { return false; } } - }, + return true + } // BR_G88 - ruleInputIsUnlocked: (sindex) => { + static ruleInputIsUnlocked(sindex: SindexEntry[]) { const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (ENTRY.isLocked) { return false; } } - }, + return true + } // BR_G89 - ruleInputIsTimeUnlocked: (sindex) => { + static ruleInputIsTimeUnlocked(sindex: SindexEntry[]) { const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (ENTRY.isTimeLocked) { return false; } } - }, + return true + } // BR_G90 - ruleOutputBase: (sindex, HEAD_1) => { + static ruleOutputBase(sindex: SindexEntry[], HEAD_1: DBHead) { const inputs = _.where(sindex, { op: constants.IDX_CREATE }); for (const ENTRY of inputs) { if (ENTRY.unitBase > HEAD_1.unitBase) { return false; } } - }, + return true + } // BR_G91 - ruleIndexGenDividend: (HEAD, dal) => co(function*() { + static async ruleIndexGenDividend(HEAD: DBHead, dal: any) { const dividends = []; if (HEAD.new_dividend) { - const members = yield dal.iindexDAL.getMembersPubkeys() + const members = await dal.iindexDAL.getMembersPubkeys() for (const MEMBER of members) { dividends.push({ op: 'CREATE', @@ -1368,24 +1548,24 @@ const indexer = module.exports = { } } return dividends; - }), + } // BR_G106 - ruleIndexGarbageSmallAccounts: (HEAD, sindex, dal) => co(function*() { + static async ruleIndexGarbageSmallAccounts(HEAD: DBHead, sindex: SindexEntry[], dal: any) { const garbages = []; - const accounts = Object.keys(sindex.reduce((acc, src) => { + const accounts = Object.keys(sindex.reduce((acc: { [k:string]: boolean }, src) => { acc[src.conditions] = true; return acc; }, {})); - const wallets = accounts.reduce((map, acc) => { + const wallets: { [k:string]: Promise<any> } = accounts.reduce((map: { [k:string]: Promise<any> }, acc) => { map[acc] = dal.getWallet(acc); return map; }, {}); for (const account of accounts) { - const localAccountEntries = _.filter(sindex, (src) => src.conditions == account); - const wallet = yield wallets[account]; + const localAccountEntries = _.filter(sindex, (src:SindexEntry) => src.conditions == account); + const wallet = await wallets[account]; const balance = wallet.balance - const variations = localAccountEntries.reduce((sum, src) => { + const variations = localAccountEntries.reduce((sum:number, src:SindexEntry) => { if (src.op === 'CREATE') { return sum + src.amount * Math.pow(10, src.base); } else { @@ -1394,9 +1574,9 @@ const indexer = module.exports = { }, 0) // console.log('Balance of %s = %s (%s)', account, balance, variations > 0 ? '+' + variations : variations) if (balance + variations < constants.ACCOUNT_MINIMUM_CURRENT_BASED_AMOUNT * Math.pow(10, HEAD.unitBase)) { - const globalAccountEntries = yield dal.sindexDAL.getAvailableForConditions(account) + const globalAccountEntries = await dal.sindexDAL.getAvailableForConditions(account) for (const src of localAccountEntries.concat(globalAccountEntries)) { - const sourceBeingConsumed = _.filter(sindex, (entry) => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0; + const sourceBeingConsumed = _.filter(sindex, (entry:SindexEntry) => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0; if (!sourceBeingConsumed) { garbages.push({ op: 'UPDATE', @@ -1416,12 +1596,12 @@ const indexer = module.exports = { } } return garbages; - }), + } // BR_G92 - ruleIndexGenCertificationExpiry: (HEAD, dal) => co(function*() { + static async ruleIndexGenCertificationExpiry(HEAD: DBHead, dal:any) { const expiries = []; - const certs = yield dal.cindexDAL.findExpired(HEAD.medianTime); + const certs = await dal.cindexDAL.findExpired(HEAD.medianTime); for (const CERT of certs) { expiries.push({ op: 'UPDATE', @@ -1434,14 +1614,14 @@ const indexer = module.exports = { }); } return expiries; - }), + } // BR_G93 - ruleIndexGenMembershipExpiry: (HEAD, dal) => co(function*() { + static async ruleIndexGenMembershipExpiry(HEAD: DBHead, dal:any) { const expiries = []; - const memberships = reduceBy(yield dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime } }), ['pub']); + const memberships: MindexEntry[] = reduceBy(await dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime } }), ['pub']); for (const POTENTIAL of memberships) { - const MS = yield dal.mindexDAL.getReducedMS(POTENTIAL.pub); + const MS = await dal.mindexDAL.getReducedMS(POTENTIAL.pub); const hasRenewedSince = MS.expires_on > HEAD.medianTime; if (!MS.expired_on && !hasRenewedSince) { expiries.push({ @@ -1455,14 +1635,14 @@ const indexer = module.exports = { } } return expiries; - }), + } // BR_G94 - ruleIndexGenExclusionByMembership: (HEAD, mindex, dal) => co(function*() { + static async ruleIndexGenExclusionByMembership(HEAD: DBHead, mindex: MindexEntry[], dal:any) { const exclusions = []; - const memberships = _.filter(mindex, (entry) => entry.expired_on); + const memberships = _.filter(mindex, (entry: MindexEntry) => entry.expired_on); for (const MS of memberships) { - const idty = yield dal.iindexDAL.getFromPubkey(MS.pub); + const idty = await dal.iindexDAL.getFromPubkey(MS.pub); if (idty.member) { exclusions.push({ op: 'UPDATE', @@ -1474,19 +1654,19 @@ const indexer = module.exports = { } } return exclusions; - }), + } // BR_G95 - ruleIndexGenExclusionByCertificatons: (HEAD, cindex, iindex, conf, dal) => co(function*() { + static async ruleIndexGenExclusionByCertificatons(HEAD: DBHead, cindex: CindexEntry[], iindex: IindexEntry[], conf: ConfDTO, dal: any) { const exclusions = []; - const expiredCerts = _.filter(cindex, (c) => c.expired_on > 0); + const expiredCerts = _.filter(cindex, (c: CindexEntry) => 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.getValidLinksTo(CERT.receiver); + const just_expired = _.filter(cindex, (c: CindexEntry) => c.receiver == CERT.receiver && c.expired_on > 0); + const just_received = _.filter(cindex, (c: CindexEntry) => c.receiver == CERT.receiver && c.expired_on == 0); + const non_expired_global = await dal.cindexDAL.getValidLinksTo(CERT.receiver); if ((count(non_expired_global) - count(just_expired) + count(just_received)) < conf.sigQty) { - const isInExcluded = _.filter(iindex, (i) => i.member === false && i.pub === CERT.receiver)[0]; - const idty = yield dal.iindexDAL.getFromPubkey(CERT.receiver); + const isInExcluded = _.filter(iindex, (i: IindexEntry) => i.member === false && i.pub === CERT.receiver)[0]; + const idty = await dal.iindexDAL.getFromPubkey(CERT.receiver); if (!isInExcluded && idty.member) { exclusions.push({ op: 'UPDATE', @@ -1499,14 +1679,14 @@ const indexer = module.exports = { } } return exclusions; - }), + } // BR_G96 - ruleIndexGenImplicitRevocation: (HEAD, dal) => co(function*() { + static async ruleIndexGenImplicitRevocation(HEAD: DBHead, dal:any) { const revocations = []; - const pending = yield dal.mindexDAL.sqlFind({ revokes_on: { $lte: HEAD.medianTime}, revoked_on: { $null: true } }); + const pending = await 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 })); + const REDUCED = reduce(await dal.mindexDAL.sqlFind({ pub: MS.pub })) if (REDUCED.revokes_on <= HEAD.medianTime && !REDUCED.revoked_on) { revocations.push({ op: 'UPDATE', @@ -1519,10 +1699,10 @@ const indexer = module.exports = { } } return revocations; - }), + } // BR_G104 - ruleIndexCorrectMembershipExpiryDate: (HEAD, mindex, dal) => co(function*() { + static async ruleIndexCorrectMembershipExpiryDate(HEAD: DBHead, mindex: MindexEntry[], dal:any) { for (const MS of mindex) { if (MS.type == 'JOIN' || MS.type == 'ACTIVE') { let basedBlock = { medianTime: 0 }; @@ -1530,59 +1710,82 @@ const indexer = module.exports = { basedBlock = HEAD; } else { if (HEAD.currency === 'gtest') { - basedBlock = yield dal.getBlockByBlockstamp(MS.created_on); + basedBlock = await dal.getBlockByBlockstamp(MS.created_on); } else { - basedBlock = yield dal.getBlockByBlockstamp(MS.created_on); + basedBlock = await dal.getBlockByBlockstamp(MS.created_on); } } + if (MS.expires_on === null) { + MS.expires_on = 0 + } + if (MS.revokes_on === null) { + MS.revokes_on = 0 + } MS.expires_on += basedBlock.medianTime; MS.revokes_on += basedBlock.medianTime; } } - }), + } // BR_G105 - ruleIndexCorrectCertificationExpiryDate: (HEAD, cindex, dal) => co(function*() { + static async ruleIndexCorrectCertificationExpiryDate(HEAD: DBHead, cindex: CindexEntry[], dal:any) { for (const CERT of cindex) { let basedBlock = { medianTime: 0 }; if (HEAD.number == 0) { basedBlock = HEAD; } else { if (HEAD.currency === 'gtest') { - basedBlock = yield dal.getBlock(CERT.created_on); + basedBlock = await dal.getBlock(CERT.created_on); } else { - basedBlock = yield dal.getBlock(CERT.created_on); + basedBlock = await 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 }), + static iindexCreate(index: IndexEntry[]): IindexEntry[] { + return _(index).filter({ index: constants.I_INDEX, op: constants.IDX_CREATE }) + } - DUP_HELPERS: { + static mindexCreate(index: IndexEntry[]): MindexEntry[] { + return _(index).filter({ index: constants.M_INDEX, op: constants.IDX_CREATE }) + } + + static iindex(index: IndexEntry[]): IindexEntry[] { + return _(index).filter({ index: constants.I_INDEX }) + } + + static mindex(index: IndexEntry[]): MindexEntry[] { + return _(index).filter({ index: constants.M_INDEX }) + } + + static cindex(index: IndexEntry[]): CindexEntry[] { + return _(index).filter({ index: constants.C_INDEX }) + } + + static sindex(index: IndexEntry[]): SindexEntry[] { + return _(index).filter({ index: constants.S_INDEX }) + } + + static DUP_HELPERS = { reduce: reduce, reduceBy: reduceBy, - getMaxBlockSize: (HEAD) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)), + getMaxBlockSize: (HEAD: DBHead) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)), checkPeopleAreNotOudistanced: checkPeopleAreNotOudistanced } -}; +} -function count(range) { +function count(range:any[]) { return range.length; } -function uniq(range) { +function uniq(range:any[]) { return _.uniq(range); } -function average(values) { +function average(values:number[]) { // No values => 0 average if (!values.length) return 0; // Otherwise, real average @@ -1590,7 +1793,7 @@ function average(values) { return Math.floor(avg); } -function median(values) { +function median(values:number[]) { let med = 0; values.sort((a, b) => a < b ? -1 : (a > b ? 1 : 0)); const nbValues = values.length; @@ -1607,16 +1810,16 @@ function median(values) { return med; } -function number(theBlockstamp) { +function number(theBlockstamp: string) { return parseInt(theBlockstamp); } -function blockstamp(aNumber, aHash) { +function blockstamp(aNumber: number, aHash: string) { return [aNumber, aHash].join('-'); } -function reduce(records) { - return records.reduce((obj, record) => { +function reduce(records: any[]) { + return records.reduce((obj:any, record) => { const keys = Object.keys(record); for (const k of keys) { if (record[k] !== undefined && record[k] !== null) { @@ -1627,170 +1830,163 @@ function reduce(records) { }, {}); } -function reduceBy(reducables, properties) { - const reduced = reducables.reduce((map, entry) => { +function reduceBy(reducables: SindexEntry[], properties: string[]): any[] { + const reduced = reducables.reduce((map: any, entry: any) => { 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)); + return _.values(reduced).map((value: SindexEntry[]) => Indexer.DUP_HELPERS.reduce(value)); } -function checkPeopleAreNotOudistanced (pubkeys, newLinks, newcomers, conf, dal) { - return co(function *() { - // let wotb = dal.wotb; - let wotb = dal.wotb.memCopy(); - 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 }); - } +async function checkPeopleAreNotOudistanced (pubkeys: string[], newLinks: any, newcomers: string[], conf: ConfDTO, dal: any) { + // let wotb = dal.wotb; + let wotb = dal.wotb.memCopy(); + let current = await 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: any, 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 = await getNodeIDfromPubkey(nodesCache, toKey, dal); + for (const fromKey of newLinks[toKey]) { + let fromNode = await getNodeIDfromPubkey(nodesCache, fromKey, dal); + tempLinks.push({ from: fromNode, to: toNode }); } - wotb.setMaxCert(conf.sigStock + tempLinks.length); - 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); - const 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; - } + } + wotb.setMaxCert(conf.sigStock + tempLinks.length); + 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 = await getNodeIDfromPubkey(nodesCache, pubkey, dal); + const 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()); - wotb.clear(); - return error ? true : false; - }); + } + // Undo temp links/nodes + tempLinks.forEach((link) => wotb.removeLink(link.from, link.to)); + newcomers.forEach(() => wotb.removeNode()); + wotb.clear(); + 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; - }); +async function getNodeIDfromPubkey(nodesCache: any, pubkey: string, dal: any) { + let toNode = nodesCache[pubkey]; + // Eventually cache the target nodeID + if (toNode === null || toNode === undefined) { + let idty = await 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; +async function sigCheckRevoke(entry: MindexEntry, dal: any, currency: string) { + try { + let pubkey = entry.pub, sig = entry.revocation; + let idty = await 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.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'); +async function checkCertificationIsValid (block: BlockDTO, cert: CindexEntry, findIdtyFunc: (b:BlockDTO,to:string,dal:any)=>Promise<IdentityDTO>, conf: ConfDTO, dal: any) { + if (block.number == 0 && cert.created_on != 0) { + throw Error('Number must be 0 for root block\'s certifications'); + } else { + try { + let basedBlock = new BlockDTO() + basedBlock.hash = constants.SPECIAL_HASH + + if (block.number != 0) { + try { + basedBlock = await dal.getBlock(cert.created_on); + } catch (e) { + throw Error('Certification based on an unexisting block'); } - 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 constants.ERRORS.WRONG_SIGNATURE_FOR_CERT - } - return true; + } + let idty = await findIdtyFunc(block, cert.receiver, dal) + let current = block.number == 0 ? null : await 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.issuer == 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.issuer, + buid: buid, + sig: '' + })); + const verified = keyring.verify(raw, cert.sig, cert.issuer); + if (!verified) { + throw constants.ERRORS.WRONG_SIGNATURE_FOR_CERT } - } catch (e) { - return false; + return true; } + } catch (e) { + return false; } - }); + } } -function txSourceUnlock(ENTRY, source, HEAD) { +function txSourceUnlock(ENTRY:SindexEntry, source:SindexEntry, HEAD: DBHead) { const tx = ENTRY.txObj; let sigResults = require('./rules').HELPERS.getSigResult(tx, 'a'); let unlocksForCondition = []; - let unlocksMetadata = {}; + let unlocksMetadata: any = {}; 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/)) { + const match = func.match(/\((.+)\)/) + let param = match && match[1]; + if (param && func.match(/SIG/)) { let pubkey = tx.issuers[parseInt(param)]; if (!pubkey) { return false; @@ -1811,7 +2007,7 @@ function txSourceUnlock(ENTRY, source, HEAD) { } if (source.conditions.match(/CSV/)) { - unlocksMetadata.elapsedTime = HEAD.medianTime - parseInt(source.written_time); + unlocksMetadata.elapsedTime = HEAD.medianTime - source.written_time; } if (unlock(source.conditions, unlocksForCondition, unlocksMetadata)) { diff --git a/app/lib/rules/global_rules.js b/app/lib/rules/global_rules.js index cc1266e05e5758be5632ab353f274c2fd37f763f..a5db1ab3db2951a506aabda0d985405dcf226bb2 100644 --- a/app/lib/rules/global_rules.js +++ b/app/lib/rules/global_rules.js @@ -3,7 +3,7 @@ const co = require('co'); const _ = require('underscore'); const common = require('duniter-common'); -const indexer = require('../indexer'); +const indexer = require('../indexer').Indexer const local_rules = require('../rules/local_rules'); const constants = common.constants @@ -48,9 +48,11 @@ rules.FUNCTIONS = { }), checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => co(function *() { - let txs = block.getTransactions(); + const txs = block.transactions const current = yield dal.getCurrentBlockOrNull(); for (const tx of txs) { + const inputs = tx.inputsAsObjects() + const outputs = tx.outputsAsObjects() let unlocks = {}; let sumOfInputs = 0; let maxOutputBase = current.unitbase; @@ -59,8 +61,8 @@ rules.FUNCTIONS = { let index = parseInt(sp[0]); unlocks[index] = sp[1]; } - for (let k = 0, len2 = tx.inputs.length; k < len2; k++) { - let src = tx.inputs[k]; + for (let k = 0, len2 = inputs.length; k < len2; k++) { + let src = inputs[k]; let dbSrc = yield dal.getSource(src.identifier, src.pos); logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, src.identifier, src.pos, dbSrc && dbSrc.consumed); if (!dbSrc && alsoCheckPendingTransactions) { @@ -132,7 +134,7 @@ rules.FUNCTIONS = { } } } - let sumOfOutputs = tx.outputs.reduce(function(p, output) { + let sumOfOutputs = outputs.reduce(function(p, output) { if (output.base > maxOutputBase) { throw constants.ERRORS.WRONG_OUTPUT_BASE; } @@ -172,7 +174,7 @@ rules.HELPERS = { checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub), checkSingleTransaction: (tx, block, conf, dal, alsoCheckPendingTransactions) => rules.FUNCTIONS.checkSourcesAvailability({ - getTransactions: () => [tx], + transactions: [tx], medianTime: block.medianTime }, conf, dal, alsoCheckPendingTransactions), diff --git a/app/lib/rules/local_rules.js b/app/lib/rules/local_rules.js index 3b7d744dd86f6082a2236333eda58147731071a3..d60afac8fe53ec58427d38bbe7cd52883d59e4c7 100644 --- a/app/lib/rules/local_rules.js +++ b/app/lib/rules/local_rules.js @@ -3,7 +3,8 @@ const co = require('co'); const _ = require('underscore'); const common = require('duniter-common'); -const indexer = require('../indexer'); +const indexer = require('../indexer').Indexer; +const BlockDTO = require('../dto/BlockDTO').BlockDTO const constants = common.constants const hashf = common.hashf @@ -276,7 +277,7 @@ rules.FUNCTIONS = { }), checkTxVersion: (block) => co(function *() { - const txs = Block.getTransactions(block); + const txs = block.transactions // Check rule against each transaction for (const tx of txs) { if (tx.version != 10) { @@ -287,7 +288,7 @@ rules.FUNCTIONS = { }), checkTxLen: (block) => co(function *() { - const txs = Block.getTransactions(block); + const txs = block.transactions // Check rule against each transaction for (const tx of txs) { const txLen = Transaction.getLen(tx); @@ -316,7 +317,7 @@ rules.FUNCTIONS = { }), checkTxIssuers: (block) => co(function *() { - const txs = Block.getTransactions(block); + const txs = block.transactions // Check rule against each transaction for (const tx of txs) { if (tx.issuers.length == 0) { @@ -327,13 +328,13 @@ rules.FUNCTIONS = { }), checkTxSources: (block) => co(function *() { - const txs = Block.getTransactions(block); - for (const tx of txs) { + const dto = BlockDTO.fromJSONObject(block) + for (const tx of dto.transactions) { if (!tx.inputs || tx.inputs.length == 0) { throw Error('A transaction must have at least 1 source'); } } - const sindex = indexer.localSIndex(block); + const sindex = indexer.localSIndex(dto); 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'); @@ -346,13 +347,13 @@ rules.FUNCTIONS = { }), checkTxAmounts: (block) => co(function *() { - for (const tx of Block.getTransactions(block)) { + for (const tx of block.transactions) { rules.HELPERS.checkTxAmountsValidity(tx); } }), checkTxRecipients: (block) => co(function *() { - const txs = Block.getTransactions(block); + const txs = block.transactions // Check rule against each transaction for (const tx of txs) { if (!tx.outputs || tx.outputs.length == 0) { @@ -360,7 +361,7 @@ rules.FUNCTIONS = { } else { // Cannot have empty output condition - for (const output of tx.outputs) { + for (const output of tx.outputsAsObjects()) { if (!output.conditions.match(/(SIG|XHX)/)) { throw Error('Empty conditions are forbidden'); } @@ -371,7 +372,7 @@ rules.FUNCTIONS = { }), checkTxSignature: (block) => co(function *() { - const txs = Block.getTransactions(block); + const txs = block.transactions // Check rule against each transaction for (const tx of txs) { let sigResult = getSigResult(tx); @@ -399,7 +400,7 @@ function getSigResult(tx) { json.unlocks = tx.unlocks; let i = 0; let signaturesMatching = true; - const raw = rawer.getTransaction(json); + const raw = tx.getRawTxNoSig() while (signaturesMatching && i < tx.signatures.length) { const sig = tx.signatures[i]; const pub = tx.issuers[i]; @@ -446,35 +447,37 @@ rules.HELPERS = { checkSingleTransactionLocally: (tx, done) => checkBunchOfTransactions([tx], done), checkTxAmountsValidity: (tx) => { + const inputs = tx.inputsAsObjects() + const outputs = tx.outputsAsObjects() // Rule of money conservation - const commonBase = tx.inputs.concat(tx.outputs).reduce((min, input) => { + const commonBase = inputs.concat(outputs).reduce((min, input) => { if (min === null) return input.base; return Math.min(min, parseInt(input.base)); }, null); - const inputSumCommonBase = tx.inputs.reduce((sum, input) => { + const inputSumCommonBase = inputs.reduce((sum, input) => { return sum + input.amount * Math.pow(10, input.base - commonBase); }, 0); - const outputSumCommonBase = tx.outputs.reduce((sum, output) => { + const outputSumCommonBase = outputs.reduce((sum, output) => { return sum + output.amount * Math.pow(10, output.base - commonBase); }, 0); if (inputSumCommonBase !== outputSumCommonBase) { throw constants.ERRORS.TX_INPUTS_OUTPUTS_NOT_EQUAL; } // Rule of unit base transformation - const maxOutputBase = tx.outputs.reduce((max, output) => { + const maxOutputBase = outputs.reduce((max, output) => { return Math.max(max, parseInt(output.base)); }, 0); // Compute deltas const deltas = {}; for (let i = commonBase; i <= maxOutputBase; i++) { - const inputBaseSum = tx.inputs.reduce((sum, input) => { + const inputBaseSum = inputs.reduce((sum, input) => { if (input.base == i) { return sum + input.amount * Math.pow(10, input.base - commonBase); } else { return sum; } }, 0); - const outputBaseSum = tx.outputs.reduce((sum, output) => { + const outputBaseSum = outputs.reduce((sum, output) => { if (output.base == i) { return sum + output.amount * Math.pow(10, output.base - commonBase); } else { diff --git a/app/modules/prover/lib/blockGenerator.js b/app/modules/prover/lib/blockGenerator.js index e0bf142f96f01f3574bbbd1016144594fe679b7c..26496eb869f4e00b308743fa541b9f03731f530f 100644 --- a/app/modules/prover/lib/blockGenerator.js +++ b/app/modules/prover/lib/blockGenerator.js @@ -4,8 +4,9 @@ const co = require('co'); const moment = require('moment'); const inquirer = require('inquirer'); const common = require('duniter-common'); -const indexer = require('../../../lib/indexer') +const indexer = require('../../../lib/indexer').Indexer const rules = require('../../../lib/rules') +const TransactionDTO = require('../../../lib/dto/TransactionDTO').TransactionDTO const keyring = common.keyring; const hashf = common.hashf; @@ -105,7 +106,7 @@ function BlockGenerator(server, prover) { const passingTxs = []; for (const obj of txs) { obj.currency = conf.currency - const tx = Transaction.fromJSON(obj); + const tx = TransactionDTO.fromJSONObject(obj); try { yield new Promise((resolve, reject) => { rules.HELPERS.checkBunchOfTransactions(passingTxs.concat(tx), (err, res) => { @@ -587,7 +588,7 @@ function BlockGenerator(server, prover) { transactions.forEach((tx) => { const txLen = Transaction.getLen(tx); if (txLen <= common.constants.MAXIMUM_LEN_OF_COMPACT_TX && blockLen + txLen <= maxLenOfBlock && tx.version == common.constants.TRANSACTION_VERSION) { - block.transactions.push({ raw: tx.compact() }); + block.transactions.push({ raw: tx.getCompactVersion() }); } blockLen += txLen; }); diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js index 3cf7231fad9c51237bb4521f789cd150bd216215..1e1ace7abf07dd960243f9fcd7b41ad161175e33 100644 --- a/app/service/BlockchainService.js +++ b/app/service/BlockchainService.js @@ -5,12 +5,13 @@ const co = require('co'); const parsers = require('duniter-common').parsers; const rules = require('../lib/rules') const constants = require('../lib/constants'); -const blockchainCtx = require('../lib/computation/blockchainContext'); +const blockchainCtx = require('../lib/computation/BlockchainContext').BlockchainContext const Block = require('../lib/entity/block'); +const BlockDTO = require('../lib/dto/BlockDTO').BlockDTO const Identity = require('../lib/entity/identity'); const Transaction = require('../lib/entity/transaction'); const AbstractService = require('./AbstractService'); -const quickSync = require('../lib/computation/quickSync') +const QuickSynchronizer = require('../lib/computation/QuickSync').QuickSynchronizer const CHECK_ALL_RULES = true; @@ -23,7 +24,7 @@ function BlockchainService (server) { AbstractService.call(this); let that = this; - const mainContext = blockchainCtx(); + const mainContext = new blockchainCtx(); let conf, dal, logger, selfPubkey, quickSynchronizer this.getContext = () => mainContext; @@ -32,7 +33,7 @@ function BlockchainService (server) { dal = newDAL; conf = newConf; logger = require('../lib/logger')(dal.profile) - quickSynchronizer = quickSync(server.blockchain, conf, dal, logger) + quickSynchronizer = new QuickSynchronizer(server.blockchain, conf, dal, logger) mainContext.setConfDAL(conf, dal, server.blockchain, quickSynchronizer) selfPubkey = newKeyPair.publicKey; }; @@ -46,7 +47,8 @@ function BlockchainService (server) { }); this.checkBlock = function(block) { - return mainContext.checkBlock(block); + const dto = BlockDTO.fromJSONObject(block) + return mainContext.checkBlock(dto); }; this.branches = () => co(function *() { @@ -129,11 +131,12 @@ function BlockchainService (server) { let followsCurrent = !current || (obj.number == current.number + 1 && obj.previousHash == current.hash); if (followsCurrent) { // try to add it on main blockchain + const dto = BlockDTO.fromJSONObject(obj) if (doCheck) { - const { index, HEAD } = yield mainContext.checkBlock(obj, constants.WITH_SIGNATURES_AND_POW); - return yield mainContext.addBlock(obj, index, HEAD) + const { index, HEAD } = yield mainContext.checkBlock(dto, constants.WITH_SIGNATURES_AND_POW); + return yield mainContext.addBlock(dto, index, HEAD) } else { - return yield mainContext.addBlock(obj) + return yield mainContext.addBlock(dto) } } else if (forkAllowed) { // add it as side chain diff --git a/app/service/TransactionsService.js b/app/service/TransactionsService.js index cd8db28f1a1616d865580d562ff67008d4772de3..bb5f89a20583dde96dc02da031289b09090fc356 100644 --- a/app/service/TransactionsService.js +++ b/app/service/TransactionsService.js @@ -6,6 +6,7 @@ const constants = require('../lib/constants'); const rules = require('../lib/rules') const Transaction = require('../lib/entity/transaction'); const AbstractService = require('./AbstractService'); +const TransactionDTO = require('../lib/dto/TransactionDTO').TransactionDTO module.exports = () => { return new TransactionService(); @@ -37,9 +38,10 @@ function TransactionService () { // Start checks... const transaction = tx.getTransaction(); const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 }; - yield Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(transaction); + const dto = TransactionDTO.fromJSONObject(tx) + yield Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(dto); yield rules.HELPERS.checkTxBlockStamp(transaction, dal); - yield rules.HELPERS.checkSingleTransaction(transaction, nextBlockWithFakeTimeVariation, conf, dal, CHECK_PENDING_TRANSACTIONS); + yield rules.HELPERS.checkSingleTransaction(dto, nextBlockWithFakeTimeVariation, conf, dal, CHECK_PENDING_TRANSACTIONS); const server_pubkey = conf.pair && conf.pair.pub; transaction.pubkey = transaction.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : ''; if (!(yield dal.txsDAL.sandbox.acceptNewSandBoxEntry(transaction, server_pubkey))) { diff --git a/test/dal/source_dal.js b/test/dal/source_dal.js index 8316a0692300708d947d2dc82283e4bf4e8ff00f..1d98a1a994e72a63a9789c22abecf3b7e0d97b08 100644 --- a/test/dal/source_dal.js +++ b/test/dal/source_dal.js @@ -3,7 +3,7 @@ const co = require('co'); const should = require('should'); const FileDAL = require('../../app/lib/dal/fileDAL'); const dir = require('../../app/lib/system/directory'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const toolbox = require('../integration/tools/toolbox'); let dal; diff --git a/test/dal/triming.js b/test/dal/triming.js index 12f5e8bbc673703efd8836176f4eb9f0cbf37a32..5d61ef39338f56943cf8e8f1edfbbab4c2b2c195 100644 --- a/test/dal/triming.js +++ b/test/dal/triming.js @@ -3,7 +3,7 @@ const co = require('co'); const should = require('should'); const FileDAL = require('../../app/lib/dal/fileDAL'); const dir = require('../../app/lib/system/directory'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const toolbox = require('../integration/tools/toolbox'); let dal; diff --git a/test/fast/block_local.js b/test/fast/block_local.js index 5d43d2e397b6036fd949ab642b2235b5f8827a00..3a62f319edab5d37eef056650b1183c7d81541e4 100644 --- a/test/fast/block_local.js +++ b/test/fast/block_local.js @@ -2,11 +2,12 @@ const co = require('co'); const should = require('should'); const parsers = require('duniter-common').parsers; -const indexer = require('../../app/lib/indexer') +const indexer = require('../../app/lib/indexer').Indexer const rules = require('../../app/lib/rules') const blocks = require('../data/blocks.js'); const parser = parsers.parseBlock; const Block = require('duniter-common').document.Block +const BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO const conf = { @@ -84,7 +85,7 @@ function test (rule, raw, expectedMessage) { return () => co(function *() { try { let obj = parser.syncWrite(raw); - let block = Block.fromJSON(obj); + let block = BlockDTO.fromJSONObject(obj); let index = indexer.localIndex(block, conf) yield rule(block, conf, index); // conf parameter is not always used if (expectedMessage) { @@ -98,7 +99,7 @@ function test (rule, raw, expectedMessage) { // This is a controlled error e.uerr.message.should.equal(expectedMessage); } else { - e.message.should.equal(expectedMessage); + // throw Error(e) // Display non wrapped errors (wrapped error is an error in constants.js) // console.error(e.stack || e); } diff --git a/test/fast/protocol-brg106-number.js b/test/fast/protocol-brg106-number.js index 9f3d0c0480145d0f488aa203225253fd52ce91c6..30cce97833b2c08d18f12ef86c07599abc8b087c 100644 --- a/test/fast/protocol-brg106-number.js +++ b/test/fast/protocol-brg106-number.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer describe("Protocol BR_G106 - Garbaging", function(){ diff --git a/test/fast/protocol-brg107-udEffectiveTime.js b/test/fast/protocol-brg107-udEffectiveTime.js index b4487b70d0a28981ab8c6420b4f94beea448fecb..7e658bbd4f90f5053dfe2235251cd564ba45176b 100644 --- a/test/fast/protocol-brg107-udEffectiveTime.js +++ b/test/fast/protocol-brg107-udEffectiveTime.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer describe("Protocol BR_G107 - udReevalTime", function(){ diff --git a/test/fast/protocol-brg11-udTime.js b/test/fast/protocol-brg11-udTime.js index c6745aeec2204cc5ed5540f49f7abd0d18d6a85c..5830cfafb88cebf24831148d0130b06b0751a3b0 100644 --- a/test/fast/protocol-brg11-udTime.js +++ b/test/fast/protocol-brg11-udTime.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer describe("Protocol BR_G11 - udTime", function(){ diff --git a/test/fast/protocol-brg13-dividend.js b/test/fast/protocol-brg13-dividend.js index cd05682445fd7077f02b596a1fc87c1dfea7e896..b691b83ce5f1f5450d1214b22ef0791784b594b7 100644 --- a/test/fast/protocol-brg13-dividend.js +++ b/test/fast/protocol-brg13-dividend.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer describe("Protocol BR_G13 - dividend", function(){ diff --git a/test/fast/protocol-brg49-version.js b/test/fast/protocol-brg49-version.js index 1121aedbc9a242606d7f8f00031bcbc5fc188c72..5c601c1aeb10114c4f11399c3a1d4a14a519ac0b 100644 --- a/test/fast/protocol-brg49-version.js +++ b/test/fast/protocol-brg49-version.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const FAIL = false; const SUCCESS = true; diff --git a/test/fast/protocol-brg50-blocksize.js b/test/fast/protocol-brg50-blocksize.js index 8f4951e88a2d401802512dec4a8951c6a0ddc3cf..4699a0ff17b3818d7f37db716b6d0e18e9c1241e 100644 --- a/test/fast/protocol-brg50-blocksize.js +++ b/test/fast/protocol-brg50-blocksize.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const FAIL = false; const SUCCESS = true; diff --git a/test/fast/protocol-brg51-number.js b/test/fast/protocol-brg51-number.js index 27e76e5aa3d7452c57e927bce7d1fece6217ef91..12d7760add6db1f3933329ac1bcd3e5121e6c57e 100644 --- a/test/fast/protocol-brg51-number.js +++ b/test/fast/protocol-brg51-number.js @@ -1,7 +1,7 @@ "use strict"; const co = require('co'); const should = require('should'); -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const FAIL = false; const SUCCESS = true; diff --git a/test/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js index 2af4f3996904f56426f4ed4a5c9038b3d4468a1c..29592f579d37299a2bbe33ba403567a2006dc543 100644 --- a/test/fast/v1.0-local-index.js +++ b/test/fast/v1.0-local-index.js @@ -3,8 +3,9 @@ const _ = require('underscore'); const should = require('should'); const parsers = require('duniter-common').parsers; -const indexer = require('../../app/lib/indexer'); +const indexer = require('../../app/lib/indexer').Indexer const constants = require('duniter-common').constants; +const BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO const raw = "Version: 10\n" + "Type: Block\n" + @@ -84,7 +85,7 @@ describe("v1.0 Local Index", function(){ before(() => { block = parsers.parseBlock.syncWrite(raw); - index = indexer.localIndex(block, { sigValidity: 100, msValidity: 40 }); + index = indexer.localIndex(BlockDTO.fromJSONObject(block), { sigValidity: 100, msValidity: 40 }); }); it('should have 30 index entries', () => { diff --git a/test/integration/branches_revert2.js b/test/integration/branches_revert2.js index 3369aac41929d004684667461e25d77d57dcd63e..5c4c93f78877bb78b25051ec4c3f6286510f92b3 100644 --- a/test/integration/branches_revert2.js +++ b/test/integration/branches_revert2.js @@ -9,6 +9,9 @@ const rp = require('request-promise'); const httpTest = require('./tools/http'); const commit = require('./tools/commit'); +require('../../app/modules/prover/lib/constants').CORES_MAXIMUM_USE_IN_PARALLEL = 1 +require('duniter-bma').duniter.methods.noLimit(); // Disables the HTTP limiter + const expectJSON = httpTest.expectJSON; const expectHttpCode = httpTest.expectHttpCode; const expectAnswer = httpTest.expectAnswer; @@ -34,6 +37,7 @@ const s1 = duniter( sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, udTime0: now + 1, + udReevalTime0: now + 1, medianTimeBlocks: 1, sigQty: 1, dt: 1, ud0: 120 }, commonConf)); @@ -58,11 +62,66 @@ describe("Revert two blocks", function() { yield commit(s1)({ time: now + 1 }); yield cat.sendP(51, toc); yield commit(s1)({ time: now + 1 }); - yield s1.revert(); }); }); - describe("Server 1 /blockchain", function() { + describe("before revert", () => { + + it('/block/0 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/0', { json: true }), { + number: 0 + }); + }); + + it('/block/2 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/2', { json: true }), { + number: 2, + dividend: 120 + }); + }); + + it('/block/3 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/3', { json: true }), { + number: 3, + dividend: null + }); + }); + + it('/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(0) + }); + }); + + it('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(2); + 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(2); + res.sources[0].should.have.property('amount').equal(120); + res.sources[1].should.have.property('identifier').equal('46D1D89CA40FBDD95A9412EF6547292CB9741DDE7D2B8A9C1D53648EFA794D44'); + res.sources[1].should.have.property('type').equal('T'); + res.sources[1].should.have.property('noffset').equal(0); + res.sources[1].should.have.property('amount').equal(51); + }); + }); + + it('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(0); + }); + }); + }) + + describe("after revert", () => { + + before(() => co(function*() { + yield s1.revert(); + })) it('/block/0 should exist', function() { return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/0', { json: true }), { @@ -109,5 +168,67 @@ describe("Revert two blocks", function() { res.sources.should.have.length(0); }); }); - }); + }) + + describe("commit again (but send less, to check that the account is not cleaned this time)", () => { + + before(() => co(function*() { + yield s1.dal.txsDAL.sqlDeleteAll() + yield cat.sendP(19, toc); + yield commit(s1)({ time: now + 1 }); + })) + + it('/block/0 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/0', { json: true }), { + number: 0 + }); + }); + + it('/block/2 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/2', { json: true }), { + number: 2, + dividend: 120 + }); + }); + + it('/block/3 should exist', function() { + return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/3', { json: true }), { + number: 3, + dividend: null + }); + }); + + it('/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(1); + res.sources[0].should.have.property('identifier').equal('7F951D4B73FB65995A1F343366A8CD3B0C76028120FD590170B251EB109926FB'); + res.sources[0].should.have.property('type').equal('T'); + res.sources[0].should.have.property('noffset').equal(1); + res.sources[0].should.have.property('amount').equal(101); + }); + }); + + it('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(2); + 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(2); + res.sources[0].should.have.property('amount').equal(120); + res.sources[1].should.have.property('identifier').equal('7F951D4B73FB65995A1F343366A8CD3B0C76028120FD590170B251EB109926FB'); + res.sources[1].should.have.property('type').equal('T'); + res.sources[1].should.have.property('noffset').equal(0); + res.sources[1].should.have.property('amount').equal(19); + }); + }); + + it('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV should have only UD', function() { + return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'), (body) => { + let res = JSON.parse(body); + res.sources.should.have.length(0); + }); + }); + }) }); diff --git a/tsconfig.json b/tsconfig.json index dc9e6be5fb085ccdd44c4b60724b8efbc39f97f3..69700a1ba0d361e92000bacffa30c87d9f5f892d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,9 @@ "moduleResolution": "node", "module": "commonjs", "strictNullChecks": true, - "noImplicitThis": true + "noImplicitThis": true, + "noImplicitAny": true, + "noImplicitReturns": true }, "include": [ "app",