diff --git a/app/lib/common-libs/underscore.ts b/app/lib/common-libs/underscore.ts new file mode 100644 index 0000000000000000000000000000000000000000..4bb08092d66a51db4d01a4733a97214086fdeae8 --- /dev/null +++ b/app/lib/common-libs/underscore.ts @@ -0,0 +1,28 @@ +const _ = require("underscore") + +export const Underscore = { + + filter: <T>(elements:T[], filterFunc: (t:T) => boolean): T[] => { + return _.filter(elements, filterFunc) + }, + + where: <T>(elements:T[], props: { [k in keyof T]?: T[k] }): T[] => { + return _.where(elements, props) + }, + + keys: <T>(map:T): (keyof T)[] => { + return _.keys(map) + }, + + values: <T>(map:{ [k:string]: T }): T[] => { + return _.values(map) + }, + + pluck: <T, K extends keyof T>(elements:T[], k:K): T[K][] => { + return _.pluck(elements, k) + }, + + uniq: <T>(elements:T[]): T[] => { + return _.uniq(elements) + } +} diff --git a/app/lib/computation/QuickSync.ts b/app/lib/computation/QuickSync.ts index 0c142ef097742beccc52535875cb7cece57c5c77..b825912f7b4113adbb6c0dfa427e31d7371cacd6 100644 --- a/app/lib/computation/QuickSync.ts +++ b/app/lib/computation/QuickSync.ts @@ -43,7 +43,7 @@ const sync_memoryDAL:AccountsGarbagingDAL = { } }, sindexDAL: { - getAvailableForConditions: (conditions:string) => null + getAvailableForConditions: (conditions:string) => Promise.resolve([]) } } @@ -100,7 +100,9 @@ export class QuickSynchronizer { async quickApplyBlocks(blocks:BlockDTO[], to: number): Promise<void> { - sync_memoryDAL.sindexDAL = { getAvailableForConditions: (conditions:string) => this.dal.sindexDAL.getAvailableForConditions(conditions) } + sync_memoryDAL.sindexDAL = { + getAvailableForConditions: (conditions:string) => this.dal.sindexDAL.getAvailableForConditions(conditions) + } await this.dal.blockDAL.insertBatch(blocks.map((b:any) => { const block = DBBlock.fromBlockDTO(b) diff --git a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts index d85975ce3a87affd9a6128cdac574e6750e11a15..ac4e52b4592baf44069d3d374caaa773c7d132d4 100644 --- a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts +++ b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts @@ -1,6 +1,6 @@ import {FullIindexEntry, IindexEntry} from "../../../indexer" import {ReduceableDAO} from "./ReduceableDAO" -import {OldIindexEntry} from "../../sqliteDAL/index/IIndexDAL" +import {OldIindexEntry} from "../../../db/OldIindexEntry" export interface IIndexDAO extends ReduceableDAO<IindexEntry> { diff --git a/app/lib/dal/indexDAL/abstract/SIndexDAO.ts b/app/lib/dal/indexDAL/abstract/SIndexDAO.ts index a740299f9b23a9447e5546f0ca19c8b2fe775f05..8f96f4ba51d41c050e966b4ea540778ce706abce 100644 --- a/app/lib/dal/indexDAL/abstract/SIndexDAO.ts +++ b/app/lib/dal/indexDAL/abstract/SIndexDAO.ts @@ -11,7 +11,7 @@ export interface SIndexDAO extends ReduceableDAO<SindexEntry> { getAvailableForPubkey(pubkey:string): Promise<{ amount:number, base:number }[]> - getAvailableForConditions(conditionsStr:string): Promise<{ amount:number, base:number }[]> + getAvailableForConditions(conditionsStr:string): Promise<SindexEntry[]> trimConsumedSource(belowNumber:number): Promise<void> diff --git a/app/lib/dal/indexDAL/loki/LokiCIndex.ts b/app/lib/dal/indexDAL/loki/LokiCIndex.ts index 077041a9030be105bba6f2689b5dd1cdee7f080b..789ac77df3dd44d76be3a6d011ea5455bc3d31d8 100644 --- a/app/lib/dal/indexDAL/loki/LokiCIndex.ts +++ b/app/lib/dal/indexDAL/loki/LokiCIndex.ts @@ -10,7 +10,7 @@ export class LokiCIndex extends LokiIndex<CindexEntry> implements CIndexDAO { } async existsNonReplayableLink(issuer: string, receiver: string): Promise<boolean> { - return Indexer.DUP_HELPERS.reduceTyped<CindexEntry>( + return Indexer.DUP_HELPERS.reduce<CindexEntry>( this.collection .chain() .find({ diff --git a/app/lib/dal/indexDAL/loki/LokiIIndex.ts b/app/lib/dal/indexDAL/loki/LokiIIndex.ts index e8576214c6d6d558c6a0f6dace7d295c38add5dc..8124c1bdbb0d2fbdbb6d2007e5903115dcfba734 100644 --- a/app/lib/dal/indexDAL/loki/LokiIIndex.ts +++ b/app/lib/dal/indexDAL/loki/LokiIIndex.ts @@ -1,9 +1,9 @@ import {FullIindexEntry, IindexEntry, Indexer} from "../../../indexer" import {IIndexDAO} from "../abstract/IIndexDAO" -import {OldIindexEntry} from "../../sqliteDAL/index/IIndexDAL" import {LokiPubkeySharingIndex} from "./LokiPubkeySharingIndex" import {getDurationInMicroSeconds, getMicrosecondsTime} from "../../../../ProcessCpuProfiler" import {NewLogger} from "../../../logger" +import {OldIindexEntry} from "../../../db/OldIindexEntry" export class LokiIIndex extends LokiPubkeySharingIndex<IindexEntry> implements IIndexDAO { diff --git a/app/lib/dal/indexDAL/loki/LokiMIndex.ts b/app/lib/dal/indexDAL/loki/LokiMIndex.ts index 9be6232def85164740d76c6adb1f2e8a4fbf9db0..5ab94e90c3ebf4644056be693d819f7509fb633c 100644 --- a/app/lib/dal/indexDAL/loki/LokiMIndex.ts +++ b/app/lib/dal/indexDAL/loki/LokiMIndex.ts @@ -39,7 +39,7 @@ export class LokiMIndex extends LokiPubkeySharingIndex<MindexEntry> implements M } async getReducedMS(pub: string): Promise<FullMindexEntry | null> { - const reducable = await this.reducable(pub) + const reducable = (await this.reducable(pub)) as (FullMindexEntry)[] if (reducable.length) { return Indexer.DUP_HELPERS.reduce(reducable) } diff --git a/app/lib/dal/indexDAL/loki/LokiSIndex.ts b/app/lib/dal/indexDAL/loki/LokiSIndex.ts index 3a8a795f84dc98293e366127a0c0c9fe2c9b41a7..d7d6bb288cc8432baf0433d3aadfa1927a287947 100644 --- a/app/lib/dal/indexDAL/loki/LokiSIndex.ts +++ b/app/lib/dal/indexDAL/loki/LokiSIndex.ts @@ -22,7 +22,7 @@ export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO { }) } - async getAvailableForConditions(conditionsStr: string): Promise<{ amount: number; base: number }[]> { + async getAvailableForConditions(conditionsStr: string): Promise<SindexEntry[]> { const sources = this.collection .chain() .find({ conditions: { $regex: conditionsStr } }) diff --git a/app/lib/dal/sqliteDAL/AbstractIndex.ts b/app/lib/dal/sqliteDAL/AbstractIndex.ts deleted file mode 100644 index 298295be161751372f971eb068d046f45fe3b531..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/AbstractIndex.ts +++ /dev/null @@ -1,61 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import {AbstractSQLite, BeforeSaveHook} from "./AbstractSQLite"; -import {SQLiteDriver} from "../drivers/SQLiteDriver"; -import {IndexEntry, Indexer} from "../../indexer"; - -const _ = require('underscore'); - -export class AbstractIndex<T extends IndexEntry> extends AbstractSQLite<T> { - - constructor( - driver:SQLiteDriver, - table: string, - pkFields: string[] = [], - fields: string[] = [], - arrays: string[] = [], - booleans: string[] = [], - bigintegers: string[] = [], - transientFields: string[] = [], - beforeSaveHook: BeforeSaveHook<T> | null = null - ) { - super(driver, table, pkFields, fields, arrays, booleans, bigintegers, transientFields, beforeSaveHook) - } - - public async init() {} - - getWrittenOn(blockstamp:string) { - return this.query('SELECT * FROM ' + this.table + ' WHERE written_on = ?', [blockstamp]) - } - - async trimRecords(belowNumber:number) { - const belowRecords:T[] = await this.query('SELECT COUNT(*) as nbRecords, pub FROM ' + this.table + ' ' + - 'WHERE CAST(written_on as int) < ? ' + - 'GROUP BY pub ' + - 'HAVING nbRecords > 1', [belowNumber]); - const reducedByPub = Indexer.DUP_HELPERS.reduceBy(belowRecords, ['pub']); - for (const record of reducedByPub) { - const recordsOfPub = await this.query('SELECT * FROM ' + this.table + ' WHERE pub = ?', [record.pub]); - const toReduce = _.filter(recordsOfPub, (rec:T) => parseInt(rec.written_on) < belowNumber); - if (toReduce.length) { - // Clean the records in the DB - await this.exec('DELETE FROM ' + this.table + ' WHERE pub = \'' + record.pub + '\''); - const nonReduced = _.filter(recordsOfPub, (rec:T) => parseInt(rec.written_on) >= belowNumber); - const reduced = Indexer.DUP_HELPERS.reduce(toReduce); - // Persist - await this.insertBatch([reduced].concat(nonReduced)); - } - } - } -} diff --git a/app/lib/dal/sqliteDAL/MetaDAL.ts b/app/lib/dal/sqliteDAL/MetaDAL.ts index 2ca460cd8d6d9a0889ba94b5768d3492785ffe0b..023259da4cd28cfb28da618488320e8f8d05b279 100644 --- a/app/lib/dal/sqliteDAL/MetaDAL.ts +++ b/app/lib/dal/sqliteDAL/MetaDAL.ts @@ -14,15 +14,8 @@ import {AbstractSQLite} from "./AbstractSQLite" import {SQLiteDriver} from "../drivers/SQLiteDriver" import {ConfDTO} from "../../dto/ConfDTO" -import {SindexEntry} from "../../indexer" -import {hashf} from "../../common" import {TransactionDTO} from "../../dto/TransactionDTO" import {IdentityDAL} from "./IdentityDAL" -import {SIndexDAL} from "./index/SIndexDAL" -import {DBBlock} from "../../db/DBBlock" -import {IdentityDTO} from "../../dto/IdentityDTO" -import {rawer} from "../../common-libs/index" -import {CommonConstants} from "../../common-libs/constants" const _ = require('underscore') const logger = require('../../logger').NewLogger('metaDAL'); @@ -207,156 +200,7 @@ export class MetaDAL extends AbstractSQLite<DBMeta> { 16: async () => {}, 17: async () => { - let blockDAL:any = new MetaDAL(this.driverCopy) - let sindexDAL = new SIndexDAL(this.driverCopy) - const blocks = await blockDAL.query('SELECT * FROM block WHERE NOT fork'); - type AmountPerKey = { - amounts: { - amount: number - comment:string - }[], - sources: { - amount:number - base:number - identifier:string - pos:number, - conditions:string - block:DBBlock, - tx:string|null - }[] - } - const amountsPerKey:{ [pub:string]: AmountPerKey[] } = {} - const members = []; - for (const b of blocks) { - const amountsInForBlockPerKey: { [pub:string]: AmountPerKey } = {}; - for (const idty of b.identities) { - members.push(IdentityDTO.fromInline(idty).pubkey) - } - if (b.dividend) { - for (const member of members) { - amountsInForBlockPerKey[member] = amountsInForBlockPerKey[member] || { amounts: [], sources: [] }; - amountsInForBlockPerKey[member].amounts.push({ amount: b.dividend * Math.pow(10, b.unitbase), comment: 'Dividend' }); - amountsInForBlockPerKey[member].sources.push({ amount: b.dividend, base: b.unitbase, identifier: member, pos: b.number, block: b, tx: null, conditions: 'SIG(' + member + ')' }); - } - } - const txs = b.transactions - for (let i = 0; i < txs.length; i++) { - const tx = txs[i]; - tx.hash = hashf(rawer.getTransaction(b.transactions[i])) - for (const input of tx.inputsAsObjects()) { - amountsInForBlockPerKey[tx.issuers[0]] = amountsInForBlockPerKey[tx.issuers[0]] || { amounts: [], sources: [] }; - amountsInForBlockPerKey[tx.issuers[0]].amounts.push({ amount: -input.amount * Math.pow(10, input.base), comment: tx.comment || '######' }); - amountsInForBlockPerKey[tx.issuers[0]].sources.push({ - amount: input.amount, - base: input.base, - identifier: input.identifier, - pos: input.pos, - conditions: "", - block: b, - tx: tx.hash - }) - } - const outputObjects = tx.outputsAsObjects() - for (let j = 0; j < outputObjects.length; j++) { - const output = outputObjects[j] - const conditions = output.conditions.match(/^SIG\((.+)\)$/); - if (conditions) { - amountsInForBlockPerKey[conditions[1]] = amountsInForBlockPerKey[conditions[1]] || { amounts: [], sources: [] }; - amountsInForBlockPerKey[conditions[1]].amounts.push({ amount: output.amount * Math.pow(10, output.base), comment: tx.comment || '######' }); - amountsInForBlockPerKey[conditions[1]].sources.push({ - amount: output.amount, - base: output.base, - identifier: tx.hash, - pos: j, - conditions: output.conditions, - block: b, - tx: tx.hash - }) - } - } - } - for (const key of Object.keys(amountsInForBlockPerKey)) { - amountsPerKey[key] = amountsPerKey[key] || []; - amountsPerKey[key].push(amountsInForBlockPerKey[key]); - } - } - const keysToSee = Object.keys(amountsPerKey); - const sourcesMovements: SindexEntry[] = []; - for (const key of keysToSee) { - const allCreates: any = {}; - const allUpdates: any = {}; - const amounts = amountsPerKey[key]; - let balance = 0; - for (let j = 0; j < amounts.length; j++) { - const amountsInBlock = amounts[j].amounts; - for (let i = 0; i < amountsInBlock.length; i++) { - const a = amountsInBlock[i].amount; - const id = [amounts[j].sources[i].identifier, amounts[j].sources[i].pos].join('-'); - if (a < 0) { - allUpdates[id] = amounts[j].sources[i]; - delete allCreates[id]; - } else { - allCreates[id] = amounts[j].sources[i]; - } - balance += a; - } - if (balance > 0 && balance < 100) { - const sourcesToDelete = []; - for (const k of Object.keys(amountsPerKey)) { - for (const packet of amountsPerKey[k]) { - for (const src of packet.sources) { - const id = [src.identifier, src.pos].join('-'); - if (src.conditions == 'SIG(' + key + ')' && allCreates[id]) { - sourcesToDelete.push(src); - } - } - } - } - const amountsToDelete = sourcesToDelete.map((src) => { - return { - amount: -src.amount * Math.pow(10, src.base), - comment: '--DESTRUCTION--' - }; - }); - amounts.splice(j + 1, 0, { amounts: amountsToDelete, sources: sourcesToDelete }); - } - } - let amountMissing = 0; - await Promise.all(_.values(allCreates).map(async (src:any) => { - const exist = await sindexDAL.getSource(src.identifier, src.pos); - if (!exist || exist.consumed) { - amountMissing += src.amount; - const block = src.block; - sourcesMovements.push({ - index: CommonConstants.I_INDEX, - op: CommonConstants.IDX_CREATE, - tx: src.tx, - identifier: src.identifier, - pos: src.pos, - unlock: null, - age: 0, - txObj: TransactionDTO.mock(), - created_on: null, - written_on: [block.number, block.hash].join('-'), - writtenOn: block.number, - written_time: block.medianTime, - locktime: src.locktime, - amount: src.amount, - base: src.base, - conditions: src.conditions, - consumed: false - }); - } - })) - let amountNotDestroyed = 0; - await Promise.all(_.values(allUpdates).map(async (src:any) => { - const exist = await sindexDAL.getSource(src.identifier, src.pos); - if (exist && !exist.consumed) { - amountNotDestroyed += src.amount; - } - })) - } - await sindexDAL.insertBatch(sourcesMovements); + // This migration is now obsolete }, 18: 'BEGIN;' + diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts deleted file mode 100644 index d0c58259a37ae6cc151f61cc5921f40c8e6649fe..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts +++ /dev/null @@ -1,151 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import {AbstractIndex} from "../AbstractIndex" -import {SQLiteDriver} from "../../drivers/SQLiteDriver" -import {CindexEntry} from "../../../indexer" -import {CommonConstants} from "../../../common-libs/constants" - -const constants = require('./../../../constants'); -const indexer = require('../../../indexer').Indexer - -export class CIndexDAL extends AbstractIndex<CindexEntry> { - - constructor(driver:SQLiteDriver) { - super( - driver, - 'c_index', - // PK fields - ['op', 'issuer', 'receiver', 'written_on'], - // Fields - [ - 'op', - 'issuer', - 'receiver', - 'created_on', - 'written_on', - 'writtenOn', - 'sig', - 'expires_on', - 'expired_on', - 'chainable_on', - 'from_wid', - 'to_wid' - ], - // Arrays - [], - // Booleans - [], - // BigIntegers - [], - // Transient - [] - ) - } - - async init() { - await this.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + this.table + ' (' + - 'op VARCHAR(10) NOT NULL,' + - 'issuer VARCHAR(50) NOT NULL,' + - 'receiver VARCHAR(50) NOT NULL,' + - 'created_on VARCHAR(80) NOT NULL,' + - 'written_on VARCHAR(80) NOT NULL,' + - 'sig VARCHAR(100) NULL,' + - 'expires_on INTEGER NULL,' + - 'expired_on INTEGER NULL,' + - 'chainable_on INTEGER NULL,' + - 'from_wid INTEGER NULL,' + - 'to_wid INTEGER NULL,' + - 'PRIMARY KEY (op,issuer,receiver,written_on)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_cindex_issuer ON c_index (issuer);' + - 'CREATE INDEX IF NOT EXISTS idx_cindex_receiver ON c_index (receiver);' + - 'CREATE INDEX IF NOT EXISTS idx_cindex_chainable_on ON c_index (chainable_on);' + - 'COMMIT;') - } - - async reducablesFrom(from:string) { - const reducables = await this.query('SELECT * FROM ' + this.table + ' WHERE issuer = ? ORDER BY CAST(written_on as integer) ASC', [from]); - return indexer.DUP_HELPERS.reduceBy(reducables, ['issuer', 'receiver', 'created_on']); - } - - async trimExpiredCerts(belowNumber:number) { - const toDelete = await this.query('SELECT * FROM ' + this.table + ' WHERE expired_on > ? AND CAST(written_on as int) < ?', [0, belowNumber]) - for (const row of toDelete) { - await this.exec("DELETE FROM " + this.table + " " + - "WHERE issuer like '" + row.issuer + "' " + - "AND receiver = '" + row.receiver + "' " + - "AND created_on like '" + row.created_on + "'"); - } - } - - getWrittenOn(blockstamp:string) { - return this.sqlFind({ written_on: blockstamp }) - } - - findExpired(medianTime:number) { - return this.query('SELECT * FROM ' + this.table + ' c1 WHERE expires_on <= ? ' + - 'AND NOT EXISTS (' + - ' SELECT * FROM c_index c2' + - ' WHERE c1.issuer = c2.issuer' + - ' AND c1.receiver = c2.receiver' + - ' AND c1.created_on = c2.created_on' + - ' AND c2.op = ?' + - ')', [medianTime, CommonConstants.IDX_UPDATE]) - } - - getValidLinksTo(receiver:string) { - return this.query('SELECT * FROM ' + this.table + ' c1 ' + - 'WHERE c1.receiver = ? ' + - 'AND c1.expired_on = 0 ' + - 'AND NOT EXISTS (' + - ' SELECT * FROM c_index c2' + - ' WHERE c1.issuer = c2.issuer' + - ' AND c1.receiver = c2.receiver' + - ' AND c1.created_on = c2.created_on' + - ' AND c2.op = ?' + - ')', [receiver, CommonConstants.IDX_UPDATE]) - } - - getValidLinksFrom(issuer:string) { - return this.query('SELECT * FROM ' + this.table + ' c1 ' + - 'WHERE c1.issuer = ? ' + - 'AND c1.expired_on = 0 ' + - 'AND NOT EXISTS (' + - ' SELECT * FROM c_index c2' + - ' WHERE c1.issuer = c2.issuer' + - ' AND c1.receiver = c2.receiver' + - ' AND c1.created_on = c2.created_on' + - ' AND c2.op = ?' + - ')', [issuer, CommonConstants.IDX_UPDATE]) - } - - async existsNonReplayableLink(issuer:string, receiver:string) { - const results = await this.query('SELECT * FROM ' + this.table + ' c1 ' + - 'WHERE c1.issuer = ? ' + - 'AND c1.receiver = ? ' + - 'AND NOT EXISTS (' + - ' SELECT * FROM c_index c2' + - ' WHERE c1.issuer = c2.issuer' + - ' AND c1.receiver = c2.receiver' + - ' AND c1.created_on = c2.created_on' + - ' AND c2.op = ?' + - ')', [issuer, receiver, CommonConstants.IDX_UPDATE]); - return results.length > 0; - } - - removeBlock(blockstamp:string) { - return this.exec('DELETE FROM ' + this.table + ' WHERE written_on = \'' + blockstamp + '\'') - } -} diff --git a/app/lib/dal/sqliteDAL/index/IIndexDAL.ts b/app/lib/dal/sqliteDAL/index/IIndexDAL.ts deleted file mode 100644 index 12c6391fb7662eeed4c163e6591956991cc08901..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/index/IIndexDAL.ts +++ /dev/null @@ -1,198 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import {SQLiteDriver} from "../../drivers/SQLiteDriver"; -import {AbstractIndex} from "../AbstractIndex"; -import {FullIindexEntry, IindexEntry, Indexer} from "../../../indexer"; - -const _ = require('underscore'); - -export interface OldIindexEntry extends IindexEntry { - pubkey: string - buid: string | null - revocation_sig:string | null -} - -export class IIndexDAL extends AbstractIndex<IindexEntry> { - - constructor(driver:SQLiteDriver) { - super( - driver, - 'i_index', - // PK fields - ['op', 'pub', 'created_on', 'written_on'], - // Fields - [ - 'op', - 'uid', - 'pub', - 'hash', - 'sig', - 'created_on', - 'written_on', - 'writtenOn', - 'member', - 'wasMember', - 'kick', - 'wotb_id' - ], - // Arrays - [], - // Booleans - ['member', 'wasMember', 'kick'], - // BigIntegers - [], - // Transient - [] - ) - } - - init() { - return this.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + this.table + ' (' + - 'op VARCHAR(10) NOT NULL,' + - 'uid VARCHAR(100) NULL,' + - 'pub VARCHAR(50) NOT NULL,' + - 'hash VARCHAR(80) NULL,' + - 'sig VARCHAR(80) NULL,' + - 'created_on VARCHAR(80) NULL,' + - 'written_on VARCHAR(80) NOT NULL,' + - 'member BOOLEAN NULL,' + - 'wasMember BOOLEAN NULL,' + - 'kick BOOLEAN NULL,' + - 'wotb_id INTEGER NULL,' + - 'PRIMARY KEY (op,pub,created_on,written_on)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_iindex_pub ON i_index (pub);' + - 'COMMIT;') - } - - async getMembers() { - // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey. - const pubkeys = await this.query('SELECT DISTINCT(pub) FROM ' + this.table); - // We get the full representation for each member - const reduced = await Promise.all(pubkeys.map(async (entry) => { - const reducable = await this.reducable(entry.pub); - return Indexer.DUP_HELPERS.reduce(reducable); - })); - // Filter on those to be kicked, return their pubkey - const filtered = _.filter(reduced, (entry:IindexEntry) => entry.member); - return filtered.map((t:IindexEntry) => this.toCorrectEntity(t)) - } - - getMembersPubkeys() { - return this.query('SELECT i1.pub ' + - 'FROM i_index i1 ' + - 'WHERE i1.member ' + - 'AND CAST(i1.written_on as int) = (' + - ' SELECT MAX(CAST(i2.written_on as int)) ' + - ' FROM i_index i2 ' + - ' WHERE i1.pub = i2.pub ' + - ' AND i2.member IS NOT NULL' + - ')') - } - - async getToBeKickedPubkeys() { - // All those who has been subject to, or who are currently subject to kicking. Make one result per pubkey. - const reducables = Indexer.DUP_HELPERS.reduceBy(await this.sqlFind({ kick: true }), ['pub']); - // We get the full representation for each member - const reduced = await Promise.all(reducables.map(async (entry) => { - const reducable = await this.reducable(entry.pub); - return Indexer.DUP_HELPERS.reduce(reducable); - })) - // Filter on those to be kicked, return their pubkey - return _.filter(reduced, (entry:IindexEntry) => entry.kick).map((entry:IindexEntry) => entry.pub); - } - - async searchThoseMatching(search:string) { - const reducables = Indexer.DUP_HELPERS.reduceBy(await this.sqlFindLikeAny({ - pub: "%" + search + "%", - uid: "%" + search + "%" - }), ['pub']); - // We get the full representation for each member - return await Promise.all(reducables.map(async (entry) => { - return this.toCorrectEntity(Indexer.DUP_HELPERS.reduce(await this.reducable(entry.pub))) - })) - } - - getFromPubkey(pubkey:string) { - return this.entityOrNull('pub', pubkey) as Promise<FullIindexEntry|null> - } - - getFromUID(uid:string, retrieveOnPubkey = false) { - return this.entityOrNull('uid', uid, retrieveOnPubkey) - } - - getFullFromPubkey(pub:string): Promise<FullIindexEntry> { - return this.entityOrNull('pub', pub) as Promise<FullIindexEntry> - } - - getFullFromUID(uid:string): Promise<FullIindexEntry|null> { - return this.entityOrNull('uid', uid, true) as Promise<FullIindexEntry|null> - } - - getFullFromHash(hash:string): Promise<FullIindexEntry|null> { - return this.entityOrNull('hash', hash, true) as Promise<FullIindexEntry|null> - } - - reducable(pub:string) { - return this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]) - } - - removeBlock(blockstamp:string) { - return this.exec('DELETE FROM ' + this.table + ' WHERE written_on = \'' + blockstamp + '\'') - } - - private async entityOrNull(field:string, value:any, retrieveOnField:boolean = false) { - let reducable = await this.query('SELECT * FROM ' + this.table + ' WHERE ' + field + ' = ?', [value]); - if (reducable.length) { - if (retrieveOnField) { - // Force full retrieval on `pub` field - reducable = await this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as int) ASC', [reducable[0].pub]); - } - return this.toCorrectEntity(Indexer.DUP_HELPERS.reduce(reducable)); - } - return null; - } - - private toCorrectEntity(row:IindexEntry): OldIindexEntry { - // Old field - return { - pubkey: row.pub, - pub: row.pub, - buid: row.created_on, - revocation_sig: null, - uid: row.uid, - hash: row.hash, - sig: row.sig, - created_on: row.created_on, - member: row.member, - wasMember: row.wasMember, - kick: row.kick, - wotb_id: row.wotb_id, - age: row.age, - index: row.index, - op: row.op, - writtenOn: row.writtenOn, - written_on: row.written_on - } - } - - async getFromPubkeyOrUid(search: string) { - const idty = await this.getFromPubkey(search) - if (idty) { - return idty - } - return this.getFromUID(search, true) as Promise<FullIindexEntry|null> - } -} diff --git a/app/lib/dal/sqliteDAL/index/MIndexDAL.ts b/app/lib/dal/sqliteDAL/index/MIndexDAL.ts deleted file mode 100644 index 29ab2c215e6c051fbdc1e8035aff7d064b42f148..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/index/MIndexDAL.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import {SQLiteDriver} from "../../drivers/SQLiteDriver"; -import {AbstractIndex} from "../AbstractIndex"; -import {FullMindexEntry, Indexer, MindexEntry} from "../../../indexer"; - -export class MIndexDAL extends AbstractIndex<MindexEntry> { - - constructor(driver:SQLiteDriver) { - super( - driver, - 'm_index', - // PK fields - ['op', 'pub', 'created_on', 'written_on'], - // Fields - [ - 'op', - 'pub', - 'created_on', - 'written_on', - 'writtenOn', - 'expires_on', - 'expired_on', - 'revokes_on', - 'revoked_on', - 'chainable_on', - 'leaving', - 'revocation' - ], - // Arrays - [], - // Booleans - ['leaving'], - // BigIntegers - [], - // Transient - [] - ) - } - - async init() { - await this.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + this.table + ' (' + - 'op VARCHAR(10) NOT NULL,' + - 'pub VARCHAR(50) NOT NULL,' + - 'created_on VARCHAR(80) NOT NULL,' + - 'written_on VARCHAR(80) NOT NULL,' + - 'expires_on INTEGER NULL,' + - 'expired_on INTEGER NULL,' + - 'revokes_on INTEGER NULL,' + - 'revoked_on INTEGER NULL,' + - 'leaving BOOLEAN NULL,' + - 'revocation VARCHAR(80) NULL,' + - 'PRIMARY KEY (op,pub,created_on,written_on)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_mindex_pub ON m_index (pub);' + - 'COMMIT;') - } - - async getReducedMS(pub:string): Promise<FullMindexEntry|null> { - const reducables = await this.reducable(pub); - if (reducables.length) { - return Indexer.DUP_HELPERS.reduce(reducables) as FullMindexEntry - } - return null - } - - reducable(pub:string) { - return this.query('SELECT * FROM ' + this.table + ' WHERE pub = ? ORDER BY CAST(written_on as integer) ASC', [pub]) -} - - async removeBlock(blockstamp:string) { - return this.exec('DELETE FROM ' + this.table + ' WHERE written_on = \'' + blockstamp + '\'') - } - - async getRevokedPubkeys() { - // All those who has been revoked. Make one result per pubkey. - const revovedMemberships = await this.sqlFind({ revoked_on: { $null: false} }); - - // Filter on those to be revoked, return their pubkey - return revovedMemberships.map((entry:MindexEntry) => entry.pub); - } -} diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts deleted file mode 100644 index 8161004f340ff0c561ce0c744e425be66e75e615..0000000000000000000000000000000000000000 --- a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts +++ /dev/null @@ -1,143 +0,0 @@ -// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1 -// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. - -import {Indexer, SindexEntry} from "../../../indexer" -import {SQLiteDriver} from "../../drivers/SQLiteDriver" -import {AbstractIndex} from "../AbstractIndex" -import {CommonConstants} from "../../../common-libs/constants" - -const _ = require('underscore'); -const constants = require('../../../constants'); - -export class SIndexDAL extends AbstractIndex<SindexEntry> { - - constructor(driver:SQLiteDriver) { - super( - driver, - 's_index', - // PK fields - ['op', 'identifier', 'pos', 'written_on'], - // Fields - [ - 'op', - 'tx', - 'identifier', - 'pos', - 'created_on', - 'written_on', - 'writtenOn', - 'written_time', - 'amount', - 'base', - 'locktime', - 'consumed', - 'conditions' - ], - // Arrays - [], - // Booleans - ['consumed'], - // BigIntegers - [], - // Transient - [] - ) - } - - async init() { - await this.exec('BEGIN;' + - 'CREATE TABLE IF NOT EXISTS ' + this.table + ' (' + - 'op VARCHAR(10) NOT NULL,' + - 'tx VARCHAR(80) NULL,' + - 'identifier VARCHAR(64) NOT NULL,' + - 'pos INTEGER NOT NULL,' + - 'created_on VARCHAR(80) NULL,' + - 'written_on VARCHAR(80) NOT NULL,' + - 'written_time INTEGER NOT NULL,' + - 'amount INTEGER NULL,' + - 'base INTEGER NULL,' + - 'locktime INTEGER NULL,' + - 'consumed BOOLEAN NOT NULL,' + - 'conditions TEXT,' + - 'PRIMARY KEY (op,identifier,pos,written_on)' + - ');' + - 'CREATE INDEX IF NOT EXISTS idx_sindex_identifier ON s_index (identifier);' + - 'CREATE INDEX IF NOT EXISTS idx_sindex_pos ON s_index (pos);' + - 'COMMIT;') - } - - async removeBlock(blockstamp:string) { - await this.exec('DELETE FROM ' + this.table + ' WHERE written_on = \'' + blockstamp + '\'') - } - - async getSource(identifier:string, pos:number) { - const reducable = await this.query('SELECT * FROM ' + this.table + ' s1 ' + - 'WHERE s1.identifier = ? ' + - 'AND s1.pos = ? ' + - 'ORDER BY op ASC', [identifier, pos]); - if (reducable.length == 0) { - return null; - } else { - const src = Indexer.DUP_HELPERS.reduce(reducable); - src.type = src.tx ? 'T' : 'D'; - return src; - } - } - - async getUDSources(pubkey:string) { - const reducables = await this.query('SELECT * FROM ' + this.table + ' s1 ' + - 'WHERE conditions = ? ' + - 'AND s1.tx IS NULL ' + - 'ORDER BY op ASC', ['SIG(' + pubkey + ')']); - const reduced = Indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos']).map((src) => { - src.type = src.tx ? 'T' : 'D'; - return src; - }); - return _.sortBy(reduced, (row:SindexEntry) => row.type == 'D' ? 0 : 1); - } - - getAvailableForPubkey(pubkey:string) { - return this.getAvailableForConditions('%SIG(' + pubkey + ')%') - } - - async getAvailableForConditions(conditionsStr:string): Promise<{ amount:number, base:number }[]> { - const potentials = await this.query('SELECT * FROM ' + this.table + ' s1 ' + - 'WHERE s1.op = ? ' + - 'AND conditions LIKE ? ' + - 'AND NOT EXISTS (' + - ' SELECT * ' + - ' FROM s_index s2 ' + - ' WHERE s2.identifier = s1.identifier ' + - ' AND s2.pos = s1.pos ' + - ' AND s2.op = ?' + - ') ' + - 'ORDER BY CAST(SUBSTR(written_on, 0, INSTR(written_on, "-")) as number)', [CommonConstants.IDX_CREATE, conditionsStr, CommonConstants.IDX_UPDATE]); - const sources = potentials.map((src) => { - src.type = src.tx ? 'T' : 'D'; - return src; - }); - return _.sortBy(sources, (row:SindexEntry) => row.type == 'D' ? 0 : 1); - } - - async trimConsumedSource(belowNumber:number) { - const toDelete = await this.query('SELECT * FROM ' + this.table + ' WHERE consumed AND CAST(written_on as int) < ?', [belowNumber]); - const queries = []; - for (const row of toDelete) { - const sql = "DELETE FROM " + this.table + " " + - "WHERE identifier like '" + row.identifier + "' " + - "AND pos = " + row.pos; - queries.push(sql); - } - await this.exec(queries.join(';\n')); - } -} diff --git a/app/lib/db/OldIindexEntry.ts b/app/lib/db/OldIindexEntry.ts new file mode 100644 index 0000000000000000000000000000000000000000..308969f906b983034a717c6503d54ac9ca1eee61 --- /dev/null +++ b/app/lib/db/OldIindexEntry.ts @@ -0,0 +1,7 @@ +import {IindexEntry} from "../indexer" + +export interface OldIindexEntry extends IindexEntry { + pubkey: string + buid: string | null + revocation_sig:string | null +} diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index 9f4e720f6527903afb6c15c44208fe0f43b95f0e..705e632dc366f9689351786d94b4dce913f265dd 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -27,8 +27,7 @@ import {FileDAL} from "./dal/fileDAL" import {DBBlock} from "./db/DBBlock" import {DBWallet} from "./db/DBWallet" import {Tristamp} from "./common/Tristamp" - -const _ = require('underscore'); +import {Underscore} from "./common-libs/underscore" const constants = CommonConstants @@ -181,18 +180,18 @@ export interface FullSindexEntry { } export interface Ranger { - (n:number, m:number, prop?:string): Promise<DBHead[]> + (n:number, m:number): Promise<DBHead[]> } -function pushIindex(index: any[], entry: IindexEntry): void { +function pushIindex(index: IndexEntry[], entry: IindexEntry): void { index.push(entry) } -function pushMindex(index: any[], entry: MindexEntry): void { +function pushMindex(index: IndexEntry[], entry: MindexEntry): void { index.push(entry) } -function pushCindex(index: any[], entry: CindexEntry): void { +function pushCindex(index: IndexEntry[], entry: CindexEntry): void { index.push(entry) } @@ -200,7 +199,7 @@ export interface AccountsGarbagingDAL { getWallet: (conditions: string) => Promise<DBWallet> saveWallet: (wallet: DBWallet) => Promise<void> sindexDAL: { - getAvailableForConditions: (conditions: string) => any + getAvailableForConditions: (conditions: string) => Promise<SindexEntry[]> } } @@ -261,7 +260,7 @@ export class Indexer { // Joiners (newcomer or join back) for (const inlineMS of block.joiners) { const ms = MembershipDTO.fromInline(inlineMS); - const matchesANewcomer = _.filter(index, (row: IindexEntry) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0; + const matchesANewcomer = Underscore.filter(index, (row: IindexEntry) => row.index == constants.I_INDEX && row.pub == ms.issuer).length > 0; if (matchesANewcomer) { // Newcomer pushMindex(index, { @@ -495,25 +494,20 @@ export class Indexer { static async quickCompleteGlobalScope(block: BlockDTO, conf: CurrencyConfDTO, bindex: DBHead[], iindex: IindexEntry[], mindex: MindexEntry[], cindex: CindexEntry[], dal:FileDAL) { - function range(start: number, end: number, property = ""): any { - let theRange; + async function range(start: number, end: number) { + let theRange:DBHead[] = [] 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; - } + theRange.reverse() + return theRange } async function head(n:number) { - return range(n, n)[0]; + return (await range(n, n))[0] } const HEAD = new DBHead() @@ -637,7 +631,7 @@ export class Indexer { if (HEAD.number > 0) { HEAD.issuerIsMember = reduce(await dal.iindexDAL.reducable(HEAD.issuer)).member; } else { - HEAD.issuerIsMember = reduce(_.where(iindex, { pub: HEAD.issuer })).member; + HEAD.issuerIsMember = reduce(Underscore.where(iindex, { pub: HEAD.issuer })).member; } // BR_G04 @@ -664,11 +658,11 @@ export class Indexer { // BR_G10 if (HEAD.number == 0) { - HEAD.membersCount = count(_.filter(iindex, (entry:IindexEntry) => entry.member === true)); + HEAD.membersCount = count(Underscore.filter(iindex, (entry:IindexEntry) => entry.member === true)); } else { HEAD.membersCount = HEAD_1.membersCount - + count(_.filter(iindex, (entry:IindexEntry) => entry.member === true)) - - count(_.filter(iindex, (entry:IindexEntry) => entry.member === false)); + + count(Underscore.filter(iindex, (entry:IindexEntry) => entry.member === true)) + - count(Underscore.filter(iindex, (entry:IindexEntry) => entry.member === false)); } // BR_G11 @@ -774,7 +768,7 @@ export class Indexer { // BR_G36 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; + const isBeingRevoked = count(Underscore.filter(mindex, m => !!(m.isBeingRevoked && m.pub == ENTRY.pub))) == 1 ENTRY.hasToBeExcluded = isMarkedAsToKick || isBeingRevoked; })) @@ -798,14 +792,14 @@ export class Indexer { // BR_G24 // Global testing, because of wotb const oneIsOutdistanced = await checkPeopleAreNotOudistanced( - _.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map((entry: MindexEntry) => entry.pub), - cindex.reduce((newLinks:any, c: CindexEntry) => { + Underscore.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map((entry: MindexEntry) => entry.pub), + cindex.reduce((newLinks, c: CindexEntry) => { newLinks[c.receiver] = newLinks[c.receiver] || []; newLinks[c.receiver].push(c.issuer); return newLinks; - }, {}), + }, <{ [k:string]: string[] }>{}), // Newcomers - _.where(iindex, { op: constants.IDX_CREATE }).map((entry: IindexEntry) => entry.pub), + Underscore.where(iindex, { op: constants.IDX_CREATE }).map((entry: IindexEntry) => entry.pub), conf, dal ); @@ -823,7 +817,7 @@ export class Indexer { })) // BR_G26 - await Promise.all(_.filter(mindex, (entry: MindexEntry) => entry.op == constants.IDX_UPDATE && entry.expired_on === 0).map(async (ENTRY: MindexEntry) => { + await Promise.all(Underscore.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; })) @@ -831,7 +825,7 @@ export class Indexer { await Promise.all(mindex.map(async (ENTRY: MindexEntry) => { if (ENTRY.type == 'JOIN' || ENTRY.type == 'ACTIVE') { const existing = count(await dal.cindexDAL.findByReceiverAndExpiredOn(ENTRY.pub, 0)) - const pending = count(_.filter(cindex, (c:CindexEntry) => c.receiver == ENTRY.pub && c.expired_on == 0)) + const pending = count(Underscore.filter(cindex, (c:CindexEntry) => c.receiver == ENTRY.pub && c.expired_on == 0)) ENTRY.enoughCerts = (existing + pending) >= conf.sigQty; } else { ENTRY.enoughCerts = true; @@ -871,7 +865,7 @@ export class Indexer { if (!ENTRY.revoked_on) { ENTRY.alreadyRevoked = false; } else { - ENTRY.alreadyRevoked = reduce(await dal.mindexDAL.reducable(ENTRY.pub)).revoked_on + ENTRY.alreadyRevoked = !!(reduce(await dal.mindexDAL.reducable(ENTRY.pub)).revoked_on) } })) @@ -912,12 +906,12 @@ export class Indexer { // BR_G42 await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { - ENTRY.toNewcomer = count(_.where(iindex, { member: true, pub: ENTRY.receiver })) > 0; + ENTRY.toNewcomer = count(Underscore.where(iindex, { member: true, pub: ENTRY.receiver })) > 0; })) // BR_G43 await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { - ENTRY.toLeaver = reduce(await dal.mindexDAL.reducable(ENTRY.receiver)).leaving + ENTRY.toLeaver = !!(reduce(await dal.mindexDAL.reducable(ENTRY.receiver)).leaving) })) // BR_G44 @@ -947,7 +941,7 @@ export class Indexer { })) // BR_G102 - await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + await Promise.all(Underscore.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { @@ -961,10 +955,10 @@ export class Indexer { })) const getInputLocalFirstOrFallbackGlobally = async (sindex:SindexEntry[], ENTRY:SindexEntry) => { - let source = _.filter(sindex, (src:SindexEntry) => + let source = Underscore.filter(sindex, src => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos - && src.conditions + && src.conditions !== '' && src.op === constants.IDX_CREATE)[0]; if (!source) { const reducable = await dal.sindexDAL.findByIdentifierPosAmountBase( @@ -979,21 +973,21 @@ export class Indexer { } // BR_G46 - await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + await Promise.all(Underscore.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY) ENTRY.conditions = source.conditions; // We valuate the input conditions, so we can map these records to a same account ENTRY.available = source.consumed === false; })) // BR_G47 - await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + await Promise.all(Underscore.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY) ENTRY.conditions = source.conditions; ENTRY.isLocked = !txSourceUnlock(ENTRY, source, HEAD); })) // BR_G48 - await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { + await Promise.all(Underscore.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => { const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY) ENTRY.isTimeLocked = ENTRY.written_time - source.written_time < ENTRY.locktime; })) @@ -1015,7 +1009,7 @@ export class Indexer { if (HEAD.number == 0) { HEAD.issuersCount = 0; } else { - HEAD.issuersCount = count(uniq(await range(1, HEAD_1.issuersFrame, 'issuer'))); // TODO + HEAD.issuersCount = count(uniq(Underscore.pluck(await range(1, HEAD_1.issuersFrame), 'issuer'))) } } @@ -1049,8 +1043,8 @@ export class Indexer { } // BR_G07 - static async prepareAvgBlockSize(HEAD: DBHead, range: (n:number,m:number,s:string)=>Promise<number[]>) { - HEAD.avgBlockSize = average(await range(1, HEAD.issuersCount, 'bsize')) + static async prepareAvgBlockSize(HEAD: DBHead, range:Ranger) { + HEAD.avgBlockSize = average(Underscore.pluck(await range(1, HEAD.issuersCount), 'bsize')) } // BR_G09 @@ -1161,11 +1155,11 @@ export class Indexer { medianOfBlocksInFrame = 1; } else { const ranged = await range(1, HEAD_1.issuersFrame) - const blocksInFrame = _.filter(ranged, (b:DBHead) => b.number <= HEAD_1.number); - const issuersInFrame = blocksInFrame.map((b:BlockDTO) => b.issuer); - blocksOfIssuer = _.filter(blocksInFrame, (entry:BlockDTO) => entry.issuer == HEAD.issuer); + const blocksInFrame = Underscore.filter(ranged, (b:DBHead) => b.number <= HEAD_1.number) + const issuersInFrame = blocksInFrame.map(b => b.issuer) + blocksOfIssuer = Underscore.filter(blocksInFrame, entry => entry.issuer == HEAD.issuer) nbPersonalBlocksInFrame = count(blocksOfIssuer); - const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer:string) => count(_.where(blocksInFrame, { issuer }))); + const blocksPerIssuerInFrame = uniq(issuersInFrame).map((issuer:string) => count(Underscore.where(blocksInFrame, { issuer }))); medianOfBlocksInFrame = Math.max(1, median(blocksPerIssuerInFrame)); if (nbPersonalBlocksInFrame == 0) { nbPreviousIssuers = 0; @@ -1191,7 +1185,7 @@ export class Indexer { // BR_G19 static async prepareIdentitiesAge(iindex: IindexEntry[], HEAD: DBHead, HEAD_1: DBHead, conf: CurrencyConfDTO, dal:FileDAL) { - await Promise.all(_.where(iindex, { op: constants.IDX_CREATE }).map(async (ENTRY: IindexEntry) => { + await Promise.all(Underscore.where(iindex, { op: constants.IDX_CREATE }).map(async (ENTRY: IindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { @@ -1207,7 +1201,7 @@ export class Indexer { // BR_G22 static async prepareMembershipsAge(mindex: MindexEntry[], HEAD: DBHead, HEAD_1: DBHead, conf: CurrencyConfDTO, dal:FileDAL) { - await Promise.all(_.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map(async (ENTRY:MindexEntry) => { + await Promise.all(Underscore.filter(mindex, (entry: MindexEntry) => !entry.revoked_on).map(async (ENTRY:MindexEntry) => { if (HEAD.number == 0 && ENTRY.created_on == '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855') { ENTRY.age = 0; } else { @@ -1562,11 +1556,11 @@ export class Indexer { static async ruleToBeKickedArePresent(iindex: IindexEntry[], dal:FileDAL) { const toBeKicked = await dal.iindexDAL.getToBeKickedPubkeys(); for (const toKick of toBeKicked) { - if (count(_.where(iindex, { pub: toKick, isBeingKicked: true })) !== 1) { + if (count(Underscore.where(iindex, { pub: toKick, isBeingKicked: true })) !== 1) { return false; } } - const beingKicked = _.filter(iindex, (i:IindexEntry) => i.member === false); + const beingKicked = Underscore.filter(iindex, (i:IindexEntry) => i.member === false); for (const entry of beingKicked) { if (!entry.hasToBeExcluded) { return false; @@ -1585,7 +1579,7 @@ export class Indexer { // BR_G87 static ruleInputIsAvailable(sindex: SindexEntry[]) { - const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + const inputs = Underscore.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (!ENTRY.available) { return false; @@ -1596,7 +1590,7 @@ export class Indexer { // BR_G88 static ruleInputIsUnlocked(sindex: SindexEntry[]) { - const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + const inputs = Underscore.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (ENTRY.isLocked) { return false; @@ -1607,7 +1601,7 @@ export class Indexer { // BR_G89 static ruleInputIsTimeUnlocked(sindex: SindexEntry[]) { - const inputs = _.where(sindex, { op: constants.IDX_UPDATE }); + const inputs = Underscore.where(sindex, { op: constants.IDX_UPDATE }); for (const ENTRY of inputs) { if (ENTRY.isTimeLocked) { return false; @@ -1618,9 +1612,9 @@ export class Indexer { // BR_G90 static ruleOutputBase(sindex: SindexEntry[], HEAD_1: DBHead) { - const inputs = _.where(sindex, { op: constants.IDX_CREATE }); + const inputs = Underscore.where(sindex, { op: constants.IDX_CREATE }); for (const ENTRY of inputs) { - if (ENTRY.unitBase > HEAD_1.unitBase) { + if (ENTRY.base > HEAD_1.unitBase) { return false; } } @@ -1658,12 +1652,12 @@ export class Indexer { acc[src.conditions] = true; return acc; }, {})); - const wallets: { [k:string]: Promise<any> } = accounts.reduce((map: { [k:string]: Promise<any> }, acc) => { + const wallets: { [k:string]: Promise<DBWallet> } = accounts.reduce((map: { [k:string]: Promise<DBWallet> }, acc) => { map[acc] = dal.getWallet(acc); return map; }, {}); for (const account of accounts) { - const localAccountEntries = _.filter(sindex, (src:SindexEntry) => src.conditions == account); + const localAccountEntries = Underscore.filter(sindex, (src:SindexEntry) => src.conditions == account); const wallet = await wallets[account]; const balance = wallet.balance const variations = localAccountEntries.reduce((sum:number, src:SindexEntry) => { @@ -1677,7 +1671,7 @@ export class Indexer { if (balance + variations < constants.ACCOUNT_MINIMUM_CURRENT_BASED_AMOUNT * Math.pow(10, HEAD.unitBase)) { const globalAccountEntries = await dal.sindexDAL.getAvailableForConditions(account) for (const src of localAccountEntries.concat(globalAccountEntries)) { - const sourceBeingConsumed = _.filter(sindex, (entry:SindexEntry) => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0; + const sourceBeingConsumed = Underscore.filter(sindex, (entry:SindexEntry) => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0; if (!sourceBeingConsumed) { garbages.push({ op: 'UPDATE', @@ -1742,7 +1736,7 @@ export class Indexer { // BR_G94 static async ruleIndexGenExclusionByMembership(HEAD: DBHead, mindex: MindexEntry[], dal:FileDAL) { const exclusions = []; - const memberships = _.filter(mindex, (entry: MindexEntry) => entry.expired_on); + const memberships = Underscore.filter(mindex, entry => !!entry.expired_on) for (const MS of memberships) { const idty = await dal.iindexDAL.getFullFromPubkey(MS.pub); if (idty.member) { @@ -1761,13 +1755,13 @@ export class Indexer { // BR_G95 static async ruleIndexGenExclusionByCertificatons(HEAD: DBHead, cindex: CindexEntry[], iindex: IindexEntry[], conf: ConfDTO, dal:FileDAL) { const exclusions = []; - const expiredCerts = _.filter(cindex, (c: CindexEntry) => c.expired_on > 0); + const expiredCerts = Underscore.filter(cindex, (c: CindexEntry) => c.expired_on > 0); for (const CERT of expiredCerts) { - 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 just_expired = Underscore.filter(cindex, (c: CindexEntry) => c.receiver == CERT.receiver && c.expired_on > 0); + const just_received = Underscore.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: IindexEntry) => i.member === false && i.pub === CERT.receiver)[0]; + const isInExcluded = Underscore.filter(iindex, (i: IindexEntry) => i.member === false && i.pub === CERT.receiver)[0]; const idty = await dal.iindexDAL.getFullFromPubkey(CERT.receiver) if (!isInExcluded && idty.member) { exclusions.push({ @@ -1865,19 +1859,18 @@ export class Indexer { static DUP_HELPERS = { reduce, - reduceTyped, reduceBy: reduceBy, getMaxBlockSize: (HEAD: DBHead) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)), checkPeopleAreNotOudistanced } } -function count(range:any[]) { +function count<T>(range:T[]) { return range.length; } -function uniq(range:any[]) { - return _.uniq(range); +function uniq(range:string[]) { + return Underscore.uniq(range) } function average(values:number[]) { @@ -1913,58 +1906,43 @@ function blockstamp(aNumber: number, aHash: string) { return [aNumber, aHash].join('-'); } -function reduce(records: any[]) { - return records.reduce((obj:any, record) => { - const keys = Object.keys(record); +function reduce<T>(records: T[]): T { + return records.reduce((obj:T, record) => { + const keys = Underscore.keys(record) for (const k of keys) { if (record[k] !== undefined && record[k] !== null) { obj[k] = record[k]; } } - return obj; - }, {}); -} - -function reduceTyped<T>(records: T[]): T { - const map:any = {} - return records.reduce((obj, record:any) => { - // Overwrite properties of the object `obj` - const keys = Object.keys(record); - for (const k of keys) { - if (record[k] !== undefined && record[k] !== null) { - obj[k] = record[k] - } - } return obj - }, map) + }, <T>{}) } -function reduceBy(reducables: IndexEntry[], 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 _.values(reduced).map((value: SindexEntry[]) => Indexer.DUP_HELPERS.reduce(value)); +function reduceBy<T extends IndexEntry>(reducables: T[], properties: (keyof T)[]): T[] { + const reduced: { [k:string]: T[] } = reducables.reduce((map, entry) => { + const id = properties.map((prop) => entry[prop]).join('-') + map[id] = map[id] || [] + map[id].push(entry) + return map + }, <{ [k:string]: T[] }>{}) + return Underscore.values(reduced).map(value => Indexer.DUP_HELPERS.reduce(value)) } -async function checkPeopleAreNotOudistanced (pubkeys: string[], newLinks: any, newcomers: string[], conf: ConfDTO, dal:FileDAL) { +async function checkPeopleAreNotOudistanced (pubkeys: string[], newLinks: { [k:string]: string[] }, newcomers: string[], conf: ConfDTO, dal:FileDAL) { // 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 nodesCache = newcomers.reduce((map, pubkey) => { let nodeID = wotb.addNode(); map[pubkey] = nodeID; wotb.setEnabled(false, nodeID); // These are not members yet return map; - }, {}); + }, <{ [k:string]: number }>{}); // Add temporarily the links to the WoT let tempLinks = []; - let toKeys = _.keys(newLinks); + let toKeys = Underscore.keys(newLinks) for (const toKey of toKeys) { let toNode = await getNodeIDfromPubkey(nodesCache, toKey, dal); for (const fromKey of newLinks[toKey]) { @@ -1992,13 +1970,13 @@ async function checkPeopleAreNotOudistanced (pubkeys: string[], newLinks: any, n return error ? true : false; } -async function getNodeIDfromPubkey(nodesCache: any, pubkey: string, dal:FileDAL) { +async function getNodeIDfromPubkey(nodesCache: { [k:string]: number }, pubkey: string, dal:FileDAL) { let toNode = nodesCache[pubkey]; // Eventually cache the target nodeID if (toNode === null || toNode === undefined) { let idty = await dal.getWrittenIdtyByPubkeyForWotbID(pubkey) - toNode = idty.wotb_id; - nodesCache[pubkey] = toNode; + toNode = idty.wotb_id + nodesCache[pubkey] = toNode } return toNode; }