diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts index 08015ba1bdbb3ea9e4233120fff4bf195178f032..1d68dd89f053981da44a860752b4da7c2bfc9f6c 100644 --- a/app/lib/blockchain/DuniterBlockchain.ts +++ b/app/lib/blockchain/DuniterBlockchain.ts @@ -239,13 +239,7 @@ export class DuniterBlockchain { await this.updateWallets(indexes.sindex, indexes.dividends, dal) if (trim) { - const TAIL = await dal.bindexDAL.tail(); - const MAX_BINDEX_SIZE = requiredBindexSizeForTail(TAIL, conf) - const currentSize = indexes.HEAD.number - TAIL.number + 1 - if (currentSize > MAX_BINDEX_SIZE) { - await dal.archiveBlocks() - await dal.trimIndexes(indexes.HEAD.number - MAX_BINDEX_SIZE); - } + await DuniterBlockchain.trimIndexes(dal, indexes.HEAD, conf) } const dbb = DBBlock.fromBlockDTO(block) @@ -573,6 +567,15 @@ export class DuniterBlockchain { throw err; } } + + public static async trimIndexes(dal: FileDAL, HEAD: { number: number }, conf: ConfDTO) { + const TAIL = await dal.bindexDAL.tail(); + const MAX_BINDEX_SIZE = requiredBindexSizeForTail(TAIL, conf) + const currentSize = HEAD.number - TAIL.number + 1 + if (currentSize > MAX_BINDEX_SIZE) { + await dal.trimIndexes(HEAD.number - MAX_BINDEX_SIZE); + } + } } export function requiredBindexSizeForTail(TAIL: { issuersCount: number, issuersFrame: number }, conf: { medianTimeBlocks: number, dtDiffEval: number, forksize: number }) { diff --git a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts index 0777ffe412241f47d4d23aa349987f9a3d6b8d37..b776a4ea7155ea9f803bb6f56cadc0b5fa699dbb 100644 --- a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts +++ b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts @@ -1,5 +1,5 @@ import {MonitorExecutionTime} from "../../../debug/MonitorExecutionTime" -import {CindexEntry, FullCindexEntry, Indexer, reduceBy} from "../../../indexer" +import {CindexEntry, FullCindexEntry, Indexer, reduce, reduceBy} from "../../../indexer" import {LevelUp} from 'levelup' import {LevelDBTable} from "./LevelDBTable" import {Underscore} from "../../../common-libs/underscore" @@ -146,13 +146,20 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C } } // Remove the "received" arrays - await Promise.all(toRemove.map(async e => { - const entry = await this.get(e.receiver) - // Remove the certification - entry.received = entry.received.filter(issuer => issuer !== e.issuer) - // Persist - await this.put(e.receiver, entry) - })) + for (const e of toRemove) { + const receiver = await this.get(e.receiver) + const issuer = await this.get(e.issuer) + const certification = reduce(issuer.issued.filter(i => i.receiver === e.receiver)) + // We remove ONLY IF no valid link still exist, i.e. we remove if the link **has expired** (we may be here because + // of a certification replay before term that is being reverted ==> in such case, even after the revert, the link + // between issuer and receiver is still valid. So don't remove it. + if (certification.expired_on) { + // Remove the certification + receiver.received = receiver.received.filter(issuer => issuer !== e.issuer) + // Persist + await this.put(e.receiver, receiver) + } + } // Remove the expires_on index entries const expires = Underscore.uniq(toRemove.filter(e => e.expires_on).map(e => e.expires_on)) await Promise.all(expires.map(async e => this.indexForExpiresOn.del(LevelDBCindex.trimExpiredOnKey(e)))) diff --git a/app/lib/debug/dump.ts b/app/lib/debug/dump.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f6731e236bf8800ac0cc621538edf5f1560eeae --- /dev/null +++ b/app/lib/debug/dump.ts @@ -0,0 +1,63 @@ +import {CindexEntry} from "../indexer" +const Table = require('cli-table') + +export function dumpBindex(rows: CindexEntry[]) { + return dump(rows, ['version','bsize','hash','issuer','time','number','membersCount','issuersCount','issuersFrame','issuersFrameVar','issuerDiff','avgBlockSize','medianTime','dividend','mass','unitBase','powMin','udTime','udReevalTime','diffNumber','speed','massReeval']) +} +export function dumpIindex(rows: CindexEntry[]) { + return dump(rows, ['op','uid','pub','hash','sig','created_on','written_on','member','wasMember','kick','wotb_id']) +} +export function dumpCindex(rows: CindexEntry[]) { + return dump(rows, ['op','issuer','receiver','created_on','written_on','sig','expires_on','expired_on','chainable_on','from_wid','to_wid','replayable_on']) +} +export function dumpCindexPretty(rows: CindexEntry[], getUid: (pub: string) => Promise<string>) { + return dump(rows, ['row','op','issuer','created_on','written_on','expires_on','expired_on','chainable_on','replayable_on'], async (f, v) => { + if (f === 'issuer') { + return await getUid(v) + } + if (f === 'written_on') { + return String(v).substr(0, 15) + } + return v + }) +} +export function dumpMindex(rows: CindexEntry[]) { + return dump(rows, ['op','pub','created_on','written_on','expires_on','expired_on','revokes_on','revoked_on','leaving','revocation','chainable_on']) +} +export function dumpSindex(rows: CindexEntry[]) { + return dump(rows, ['op','tx','identifier','pos','created_on','amount','base','locktime','consumed','conditions', 'writtenOn']) +} + +async function dump(rows: any[], columns: string[], transform: (field: string, value: any) => Promise<string> = (f, v) => Promise.resolve(v)) { + // Table columns + const t = new Table({ + head: columns, chars: {'mid': '', 'left-mid': '', 'mid-mid': '', 'right-mid': ''} + }); + let i = 0; + for (const row of rows) { + t.push(await Promise.all(columns.map(async (c) => { + if (c === 'row') { + return i + } + else if (row[c] === null) { + return "NULL" + } + else if (row[c] === undefined) { + return 'NULL' + } + else if (typeof row[c] === 'boolean') { + const v = await transform(c, row[c] ? 1 : 0) + return v + } + const v = await transform(c, row[c]) + return v + }))); + i++ + } + try { + const dumped = t.toString() + console.log(dumped) + } catch (e) { + console.error(e) + } +} diff --git a/app/modules/dump.ts b/app/modules/dump.ts index 95148fd221bc4aad253f92de17595da9a70f3a1c..295b93b5b4fbd53afaa827a4b4115e9e4a4760a2 100644 --- a/app/modules/dump.ts +++ b/app/modules/dump.ts @@ -11,11 +11,12 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. +import {exec} from "child_process" import {ConfDTO} from "../lib/dto/ConfDTO" import {Server} from "../../server" import {moment} from "../lib/common-libs/moment" import {DBBlock} from "../lib/db/DBBlock" -import {SindexEntry} from "../lib/indexer" +import {FullIindexEntry, IindexEntry, SindexEntry} from "../lib/indexer" import {BlockDTO} from "../lib/dto/BlockDTO" import {Underscore} from "../lib/common-libs/underscore" import {dumpWotWizard} from "./dump/wotwizard/wotwizard.dump" @@ -23,6 +24,13 @@ import {OtherConstants} from "../lib/other_constants" import {Querable, querablep} from "../lib/common-libs/querable" import {dumpBlocks, dumpForks} from "./dump/blocks/dump.blocks" import {newResolveTimeoutPromise} from "../lib/common-libs/timeout-promise" +import {LevelDBIindex} from "../lib/dal/indexDAL/leveldb/LevelDBIindex" +import {dumpBindex, dumpCindex, dumpCindexPretty, dumpIindex, dumpMindex, dumpSindex} from "../lib/debug/dump" +import {readFileSync} from "fs" +import {IdentityDTO} from "../lib/dto/IdentityDTO" +import {CertificationDTO, ShortCertificationDTO} from "../lib/dto/CertificationDTO" +import {MembershipDTO} from "../lib/dto/MembershipDTO" +import {RevocationDTO, ShortRevocation} from "../lib/dto/RevocationDTO" const Table = require('cli-table') @@ -55,16 +63,46 @@ module.exports = { }, cli: [{ + name: 'current', + desc: 'Shows current block\'s blockstamp', + logs: false, + preventIfRunning: true, + + onDatabaseExecute: async (server:Server) => { + const current = await server.dal.getCurrentBlockOrNull() + if (!current) { + return console.log('None') + } + const blockstamp = `${current.number}-${current.hash}` + console.log(blockstamp) + // Save DB + await server.disconnect(); + } + }, { + name: 'trim-indexes', + desc: 'Force trimming of indexes', + logs: true, + preventIfRunning: true, + + onConfiguredExecute: async (server:Server) => { + await server.dal.init(server.conf) + await server.BlockchainService.trimIndexes() + // Save DB + await server.disconnect(); + } + }, { name: 'dump [what] [name] [cond]', desc: 'Dumps data of the blockchain.', logs: false, preventIfRunning: true, - onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => { + onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => { const what: string = params[0] || '' const name: string = params[1] || '' const cond: string = params[2] || '' + await server.dal.init(server.conf) + try { switch (what) { @@ -111,6 +149,64 @@ module.exports = { // Save DB await server.disconnect(); } + }, { + name: 'search [pattern]', + desc: 'Dumps data of the blockchain matching given pattern.', + logs: false, + preventIfRunning: true, + + onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => { + const pattern: string = params[0] || '' + + try { + + const files: string[] = await new Promise<string[]>((res, rej) => exec(`grep -r ${pattern} ${server.home}/${server.conf.currency} -l | grep .json`, (err, stdout) => { + if (err) return rej(err) + console.log(stdout) + res(stdout.split('\n').filter(l => l)) + })) + + const blocks = Underscore.sortBy(await findBlocksMatching(pattern, files), b => b.number) + + const events: { b: BlockDTO, event: (IdentityDTO|ShortCertificationDTO|MembershipDTO|ShortRevocation|{ type: 'exclusion', pub: string }) }[] = [] + for (const b of blocks) { + b.identities.filter(i => i.includes(pattern)).forEach(i => { + events.push({ b, event: IdentityDTO.fromInline(i) }) + }) + b.certifications.filter(c => c.includes(pattern)).forEach(c => { + events.push({ b, event: CertificationDTO.fromInline(c) }) + }) + b.joiners.concat(b.actives).concat(b.leavers).filter(m => m.includes(pattern)).forEach(m => { + events.push({ b, event: MembershipDTO.fromInline(m) }) + }) + b.revoked.filter(m => m.includes(pattern)).forEach(r => { + events.push({ b, event: RevocationDTO.fromInline(r)Â }) + }) + b.excluded.filter(m => m.includes(pattern)).forEach(r => { + events.push({ b, event: { type: 'exclusion', pub: r }Â }) + }) + } + + for (const e of events) { + if ((e.event as IdentityDTO).uid) { + const date = await getDateForBlock(e.b) + const idty = e.event as IdentityDTO + console.log('%s: new identity %s (created on %s)', date, idty.uid, await getDateFor(server, idty.buid as string)) + } + if ((e.event as { type: 'exclusion', pub: string }).type === 'exclusion') { + const date = await getDateForBlock(e.b) + console.log('%s: excluded', date) + } + } + + console.log(events.map(e => e.event)) + + } catch (e) { + console.error(e) + } + // Save DB + await server.disconnect(); + } }, { name: 'dump-ww', desc: 'Dumps WotWizard export.', @@ -121,6 +217,21 @@ module.exports = { } } +async function findBlocksMatching(pattern: string, files: string[]) { + const matchingBlocks: BlockDTO[] = [] + for (const f of files) { + const blocks: any[] = JSON.parse(await readFileSync(f, 'utf8')).blocks + for (const jsonBlock of blocks) { + const b = BlockDTO.fromJSONObject(jsonBlock) + const raw = b.getRawSigned() + if (raw.includes(pattern)) { + matchingBlocks.push(b) + } + } + } + return matchingBlocks +} + async function dumpCurrent(server: Server) { const current = await server.dal.getCurrentBlockOrNull() if (!current) { @@ -164,8 +275,7 @@ async function dumpTable(server: Server, name: string, condition?: string) { switch (name) { case 'b_index': rows = await server.dal.bindexDAL.findRawWithOrder(criterion, [['number', false]]) - dump(rows, ['version','bsize','hash','issuer','time','number','membersCount','issuersCount','issuersFrame','issuersFrameVar','issuerDiff','avgBlockSize','medianTime','dividend','mass','unitBase','powMin','udTime','udReevalTime','diffNumber','speed','massReeval']) - break + return dumpBindex(rows) /** * Dumps issuers visible in current bindex @@ -178,59 +288,39 @@ async function dumpTable(server: Server, name: string, condition?: string) { case 'i_index': rows = await server.dal.iindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['wotb_id', false]]) - dump(rows, ['op','uid','pub','hash','sig','created_on','written_on','member','wasMember','kick','wotb_id']) - break + return dumpIindex(rows) case 'm_index': rows = await server.dal.mindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['pub', false]]) - dump(rows, ['op','pub','created_on','written_on','expires_on','expired_on','revokes_on','revoked_on','leaving','revocation','chainable_on']) - break + return dumpMindex(rows) case 'c_index': rows = await server.dal.cindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['issuer', false], ['receiver', false]]) - dump(rows, ['op','issuer','receiver','created_on','written_on','sig','expires_on','expired_on','chainable_on','from_wid','to_wid','replayable_on']) + return dumpCindex(rows) break case 's_index': const rowsTX = await server.dal.sindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['identifier', false], ['pos', false]]) const rowsUD = await server.dal.dividendDAL.findForDump(criterion) rows = rowsTX.concat(rowsUD) sortSindex(rows) - dump(rows, ['op','tx','identifier','pos','created_on','amount','base','locktime','consumed','conditions', 'writtenOn']) - break + return dumpSindex(rows) + + case 'c_index_pretty': + rows = await server.dal.cindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['issuer', false], ['receiver', false]]) + rows = rows.filter((row: any) => Object.entries(criterion).reduce((ok, crit: any) => ok && row[crit[0]] === crit[1], true)) + await dumpCindexPretty(rows, async (pub) => { + const iindexEntry = await server.dal.getWrittenIdtyByPubkey(pub) + return (iindexEntry as IindexEntry).uid as string + }) + default: console.error(`Unknown dump table ${name}`) break } } -function dump(rows: any[], columns: string[]) { - // Table columns - const t = new Table({ - head: columns - }); - for (const row of rows) { - t.push(columns.map((c) => { - if (row[c] === null) { - return "NULL" - } - else if (row[c] === undefined) { - return 'NULL' - } - else if (typeof row[c] === 'boolean') { - return row[c] ? 1 : 0 - } - return row[c] - })); - } - try { - const dumped = t.toString() - console.log(dumped) - } catch (e) { - console.error(e) - } -} - async function dumpHistory(server: Server, pub: string) { - const irows = await server.dal.iindexDAL.findRawWithOrder({ pub }, [['writtenOn', false]]) - const mrows = await server.dal.mindexDAL.findRawWithOrder({ pub }, [['writtenOn', false]]) + const irows = (await server.dal.iindexDAL.findRawWithOrder({ pub }, [['writtenOn', false]])).filter(r => pub ? r.pub === pub : true) + const mrows = (await server.dal.mindexDAL.findRawWithOrder({ pub }, [['writtenOn', false]])).filter(r => pub ? r.pub === pub : true) + const crows = (await server.dal.cindexDAL.findRawWithOrder({ pub }, [['writtenOn', false]])).filter(r => pub ? r.issuer === pub || r.receiver === pub: true) console.log('----- IDENTITY -----') for (const e of irows) { const date = await getDateFor(server, e.written_on) @@ -259,6 +349,28 @@ async function dumpHistory(server: Server, pub: string) { console.log('Non displayable MINDEX entry') } } + console.log('----- CERTIFICATION -----') + crows.forEach(crow => { + console.log(JSON.stringify(crow)) + }) + for (const e of crows) { + const dateW = await getDateFor(server, e.written_on) + const dateC = await getDateForBlockNumber(server, e.created_on) + if (e.receiver === pub) { + const issuer = await server.dal.getWrittenIdtyByPubkey(e.issuer) as FullIindexEntry + if (e.op === 'UPDATE') { + console.log('%s : %s: from %s (update)', dateC, dateW, issuer.uid) + } + else { + console.log('%s : %s: from %s', dateC, dateW, issuer.uid) + } + // } else if (e.issuer === pub) { + // const receiver = await server.dal.getWrittenIdtyByPubkey(e.receiver) as FullIindexEntry + // console.log('%s: to ', date, receiver.uid) + } else { + // console.log('Non displayable CINDEX entry') + } + } } async function dumpWot(server: Server) { @@ -274,6 +386,19 @@ async function getDateFor(server: Server, blockstamp: string) { return formatTimestamp(b.medianTime) + ' (#' + bnumberPadded + ')' } +async function getDateForBlockNumber(server: Server, number: number) { + const b = (await server.dal.getBlock(number)) as DBBlock + const s = " " + b.number + const bnumberPadded = s.substr(s.length - 6) + return formatTimestamp(b.medianTime) + ' (#' + bnumberPadded + ')' +} + +async function getDateForBlock(b: BlockDTO) { + const s = " " + b.number + const bnumberPadded = s.substr(s.length - 6) + return formatTimestamp(b.medianTime) + ' (#' + bnumberPadded + ')' +} + function formatTimestamp(ts: number) { return moment(ts * 1000).format('YYYY-MM-DD hh:mm:ss') } diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts index f22103495ae29609e5f64b6723f53ca5a1f76084..547ded67434f0b1a16024cabfdb852c85ce401a3 100644 --- a/app/modules/prover/lib/blockGenerator.ts +++ b/app/modules/prover/lib/blockGenerator.ts @@ -460,7 +460,7 @@ export class BlockGenerator { exclusions:any, wereExcluded:any, transactions:any, - manualValues:any) { + manualValues:ForcedBlockValues) { if (manualValues && manualValues.excluded) { exclusions = manualValues.excluded; @@ -630,6 +630,16 @@ export class BlockGenerator { } } + // Forced joiners (by tests) + if (manualValues && manualValues.joiners) { + block.joiners = block.joiners.concat(manualValues.joiners.map(j => j.inline())) + } + + // Forced certifications (by tests) + if (manualValues && manualValues.certifications) { + block.certifications = block.certifications.concat(manualValues.certifications.map(c => c.inline())) + } + // Final number of members block.membersCount = previousCount + block.joiners.length - block.excluded.length; @@ -679,7 +689,7 @@ export class BlockGenerator { block.issuersFrameVar = vHEAD.issuersFrameVar; // Manual values before hashing if (manualValues) { - Underscore.extend(block, Underscore.omit(manualValues, 'time')); + Underscore.extend(block, Underscore.omit(manualValues, 'time', 'certifications', 'joiners')); } // InnerHash block.time = block.medianTime; @@ -847,3 +857,13 @@ class ManualRootGenerator implements BlockGeneratorInterface { } } } + +export interface ForcedBlockValues { + time?: number + version?: number + medianTime?: number + excluded?: string[] + revoked?: string[] + certifications?: CertificationDTO[] + joiners?: MembershipDTO[] +} \ No newline at end of file diff --git a/app/service/BlockchainService.ts b/app/service/BlockchainService.ts index ccd46bd069ebd7d0574e5a5fd0f7658aa149210b..e159bed2758990bd5f749ad1ddd652e19912e160 100644 --- a/app/service/BlockchainService.ts +++ b/app/service/BlockchainService.ts @@ -459,4 +459,10 @@ export class BlockchainService extends FIFOService { return this.dal.getBlocksBetween(from, from + count - 1); } + async trimIndexes() { + const HEAD = await this.dal.getCurrentBlockOrNull() + if (HEAD) { + return DuniterBlockchain.trimIndexes(this.dal, HEAD, this.conf) + } + } } diff --git a/test/integration/fork-resolution/block-with-comebacker-revert.ts b/test/integration/fork-resolution/block-with-comebacker-revert.ts new file mode 100644 index 0000000000000000000000000000000000000000..d88d1613068db92a98b0560a1f86f9b6bc6939b7 --- /dev/null +++ b/test/integration/fork-resolution/block-with-comebacker-revert.ts @@ -0,0 +1,117 @@ +// 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 {assertEqual, writeBasicTestWithConfAnd2Users} from "../tools/test-framework" +import {CommonConstants} from "../../../app/lib/common-libs/constants" + +const currentVersion = CommonConstants.BLOCK_GENERATED_VERSION + +describe('Block revert with a comebacker in it', () => writeBasicTestWithConfAnd2Users({ + sigQty: 2, + sigReplay: 0, + sigPeriod: 0, + sigValidity: 10, + dtDiffEval: 1, + forksize: 0, +}, (test) => { + + const now = 1500000000 + + test('(t = 0) should init with a 4 members WoT with bidirectionnal certs', async (s1, cat, tac, toc) => { + CommonConstants.BLOCK_GENERATED_VERSION = 11 + await cat.createIdentity() + await tac.createIdentity() + await toc.createIdentity() + await cat.cert(tac) + await cat.cert(toc) + await tac.cert(cat) + await tac.cert(toc) + await toc.cert(cat) + await toc.cert(tac) + await cat.join() + await tac.join() + await toc.join() + const b0 = await s1.commit({ time: now }) + assertEqual(b0.certifications.length, 6) + const b1 = await s1.commit({ time: now }) + assertEqual(b1.membersCount, 3) + }) + + test('(t = 5) cat & tac certify each other', async (s1, cat, tac, toc) => { + await s1.commit({ time: now + 5 }) + await s1.commit({ time: now + 5 }) + await new Promise(resolve => setTimeout(resolve, 500)) + // cat and tac certify each other to stay in the WoT + await tac.cert(cat) + await toc.cert(cat) // <-- toc adds the 2nd certification + const b1 = await s1.commit({ time: now + 6 }) + assertEqual(b1.certifications.length, 2) + await cat.cert(tac) + await toc.cert(tac) // <-- toc adds the 2nd certification + const b2 = await s1.commit({ time: now + 6 }) + assertEqual(b2.certifications.length, 2) + // // /!\/!\/!\ + // // toc gets certified by cat, to a have a remaining valid certification in the blockchain: THIS WONT BE ENOUGH! + await cat.cert(toc) + // // /!\/!\/!\ + const b4 = await s1.commit({ time: now + 6 }) + assertEqual(b4.certifications.length, 1) + }) + + test('(t = 12) toc is excluded for lack of certifications', async (s1, cat, tac, toc) => { + await s1.commit({ time: now + 12 }) + await s1.commit({ time: now + 12 }) + const b = await s1.commit({ time: now + 12 }) + assertEqual(b.excluded.length, 1) + assertEqual(b.excluded[0], toc.pub) + }) + + test('(t = 12) we want some blocs to trim CINDEX', async (s1) => { + for (let i = 0; i < 10; i++) { + await s1.commit({ time: now + 12 }) + } + }) + + test('(t = 13 #1) toc is coming back with 2 certs, whose 1 is a renewal', async (s1, cat, tac, toc) => { + await s1.commit({ time: now + 13 }) + await s1.commit({ time: now + 13 }) + const c1 = await cat.makeCert(toc) // <-- a renewal ==> this is what we want to observe + const join = await toc.makeMembership('IN') + const b = await s1.commit({ + time: now + 13, + joiners: [join], + certifications: [c1] + }) + assertEqual(b.membersCount, 3) + assertEqual(b.number, 22) + }) + + test('(t = 12 #2) revert successfuly', async (s1) => { + await s1.revert() + const b = await s1.dal.getBlockCurrent() + assertEqual(b.membersCount, 2) + assertEqual(b.number, 21) + }) + + test('(t = 12 #3) resolution should work', async (s1) => { + await s1.resolve() + const b = await s1.dal.getBlockCurrent() + assertEqual(b.membersCount, 3) + assertEqual(b.number, 22) + }) + + after(() => { + CommonConstants.BLOCK_GENERATED_VERSION = currentVersion + }) +})) + diff --git a/test/integration/protocol/v0.5-transactions.ts b/test/integration/protocol/v0.5-transactions.ts index 63863a77f3fd8892ffa1bede279647a2cef5ee94..d950cbc8179989fe1e58c1b304977ef30e54ffb6 100644 --- a/test/integration/protocol/v0.5-transactions.ts +++ b/test/integration/protocol/v0.5-transactions.ts @@ -17,13 +17,15 @@ import {BlockDTO} from "../../../app/lib/dto/BlockDTO" const should = require('should'); const constants = require('../../../app/lib/constants'); +const now = 1578540000; + const conf = { + udTime0: now, dt: 30, avgGenTime: 5000, medianTimeBlocks: 1 // The medianTime always equals previous block's medianTime }; -const now = 1578540000; let s1:TestingServer diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts index 39e3669e5d5fe3eb7ea4a1af07214ad6ae5aecf4..40d4a36c22f4074ddd34984c360c2086fcc2ce83 100644 --- a/test/integration/tools/toolbox.ts +++ b/test/integration/tools/toolbox.ts @@ -60,6 +60,7 @@ import {WebSocketServer} from "../../../app/lib/common-libs/websocket" import {CommonConstants} from "../../../app/lib/common-libs/constants" import {WS2PRequester} from "../../../app/modules/ws2p/lib/WS2PRequester" import {WS2PDependency} from "../../../app/modules/ws2p/index" +import {ForcedBlockValues} from "../../../app/modules/prover/lib/blockGenerator" const assert = require('assert'); const rp = require('request-promise'); @@ -239,7 +240,7 @@ export const NewTestingServer = (conf:any) => { remoteipv4: host, currency: conf.currency || CURRENCY_NAME, httpLogs: true, - forksize: conf.forksize || 3, + forksize: conf.forksize !== undefined ? conf.forksize : 3, nonWoTPeersLimit: CommonConstants.DEFAULT_NON_WOT_PEERS_LIMIT, }; if (conf.sigQty === undefined) { @@ -508,7 +509,7 @@ export class TestingServer { return proven } - async commit(options:any = null) { + async commit(options:ForcedBlockValues|null = null) { const proven = await this.generateNext(options) await this.server.writeBlock(proven, true, true) // The resolution is done manually const blocksResolved = await this.server.BlockchainService.blockResolution() diff --git a/test/unit-tools.ts b/test/unit-tools.ts index c154cc34c32e8f6d594ea9dfa55eb3b4e3c05596..e7bbf1aba4dc7a2301f99da554d8465852055739 100644 --- a/test/unit-tools.ts +++ b/test/unit-tools.ts @@ -12,6 +12,7 @@ // GNU Affero General Public License for more details. import * as assert from 'assert' + const should = require('should') export async function shouldThrow(promise:Promise<any>) { @@ -58,7 +59,7 @@ export const assertThrows = async (promise:Promise<any>, message:string|null = n if (e === "Should have thrown") { throw e } - assert.equal(e, message) + assert.equal(e.message || e, message) } }