diff --git a/app/lib/constants.ts b/app/lib/constants.ts index 7092d69e5ff3ccb15b66161198ad571936edd2ac..ee55efaa0229b188d87e50d1018478c667655d12 100644 --- a/app/lib/constants.ts +++ b/app/lib/constants.ts @@ -146,6 +146,7 @@ module.exports = { STEPMAX: 3, SIGDELAY: 3600 * 24 * 365 * 5, SIGPERIOD: 0, // Instant + SIGREPLAY: 0, // Instant MSPERIOD: 0, // Instant SIGSTOCK: 40, SIGWINDOW: 3600 * 24 * 7, // a week diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index be9960bb138709210277ea918c215fbcd94cc7b8..36603d84f974792f3a93c3ecefb3adb363b7f26e 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -396,6 +396,11 @@ export class FileDAL { return this.blockDAL.getCountOfBlocksIssuedBy(issuer) } + /** + * Find all the blocks in the blockchain whose number is between [start ; end] + * @param start Lower number bound (included). + * @param end Higher number bound (included). + */ async getBlocksBetween (start:number, end:number) { start = Math.max(0, start) end= Math.max(0, end) @@ -878,8 +883,8 @@ export class FileDAL { .value(); } - existsNonReplayableLink(from:string, to:string) { - return this.cindexDAL.existsNonReplayableLink(from, to) + existsNonReplayableLink(from:string, to:string, medianTime: number, version: number) { + return this.cindexDAL.existsNonReplayableLink(from, to, medianTime, version) } async getSource(identifier:string, pos:number, isDividend: boolean): Promise<SimpleTxInput | null> { @@ -919,10 +924,13 @@ export class FileDAL { return (ms && ms.leaving) || false; } - async existsCert(cert:any) { + async existsCert(cert: DBCert, current: DBBlock|null) { const existing = await this.certDAL.existsGivenCert(cert); if (existing) return existing; - const existsLink = await this.cindexDAL.existsNonReplayableLink(cert.from, cert.to); + if (!current) { + return false + } + const existsLink = await this.cindexDAL.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version) return !!existsLink; } diff --git a/app/lib/dal/indexDAL/abstract/CIndexDAO.ts b/app/lib/dal/indexDAL/abstract/CIndexDAO.ts index 3f1cefa2b9889e67ebcd2d3e7c15574e73b7166d..2998c6b41f7b43cc8b9cb87235f45390c6206c23 100644 --- a/app/lib/dal/indexDAL/abstract/CIndexDAO.ts +++ b/app/lib/dal/indexDAL/abstract/CIndexDAO.ts @@ -15,7 +15,7 @@ export interface CIndexDAO extends ReduceableDAO<CindexEntry> { findByReceiverAndExpiredOn(pub: string, expired_on: number): Promise<CindexEntry[]> - existsNonReplayableLink(issuer:string, receiver:string): Promise<boolean> + existsNonReplayableLink(issuer:string, receiver:string, medianTime: number, version: number): Promise<boolean> getReceiversAbove(minsig: number): Promise<string[]> diff --git a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts index d8dfd0ada7b98bdc01538ffac6a733c37816fa59..972f4181f3bf61359a8e23aa903a65ad555bc6ff 100644 --- a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts +++ b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts @@ -88,7 +88,7 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C issuers = Underscore.uniq(issuers) await Promise.all(issuers.map(async issuer => { const entry = await this.get(issuer) - const fullEntries = reduceBy(entry.issued, ['issuer', 'receiver', 'created_on']) + const fullEntries = reduceBy(entry.issued, ['issuer', 'receiver']) const toRemove: string[] =Â [] // We remember the maximum value of expired_on, for efficient trimming search fullEntries @@ -190,10 +190,10 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C } } - async existsNonReplayableLink(issuer: string, receiver: string): Promise<boolean> { + async existsNonReplayableLink(issuer: string, receiver: string, medianTime: number, version: number): Promise<boolean> { const entries = await this.findByIssuer(issuer) - const reduced = Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver', 'created_on']) - return reduced.filter(e => e.receiver === receiver && !e.expired_on).length > 0 + const reduced = Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver']) + return reduced.filter(e => e.receiver === receiver && (version <= 10 || e.replayable_on >= medianTime)).length > 0 } async findByIssuer(issuer: string): Promise<CindexEntry[]> { @@ -219,7 +219,7 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C async findExpiresOnLteNotExpiredYet(medianTime: number): Promise<CindexEntry[]> { const issuers: string[] = Underscore.uniq((await this.indexForExpiresOn.findAllValues({ lte: LevelDBCindex.trimExpiredOnKey(medianTime) })).reduce(reduceConcat, [])) return (await Promise.all(issuers.map(async issuer => { - const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver', 'created_on']) + const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver']) return fullEntries.filter(e => e.expires_on <= medianTime && !e.expired_on) }))).reduce(reduceConcat, []) } @@ -229,21 +229,21 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C } async getValidLinksFrom(issuer: string): Promise<CindexEntry[]> { - const fullEntries = Indexer.DUP_HELPERS.reduceBy(((await this.getOrNull(issuer)) || { issued: [] }).issued, ['issuer', 'receiver', 'created_on']) + const fullEntries = Indexer.DUP_HELPERS.reduceBy(((await this.getOrNull(issuer)) || { issued: [] }).issued, ['issuer', 'receiver']) return fullEntries.filter(e => !e.expired_on) } async getValidLinksTo(receiver: string): Promise<CindexEntry[]> { const issuers: string[] = ((await this.getOrNull(receiver)) || { issued: [], received: [] }).received return (await Promise.all(issuers.map(async issuer => { - const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver', 'created_on']) + const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver']) return fullEntries.filter(e => e.receiver === receiver && !e.expired_on) }))).reduce(reduceConcat, []) } async reducablesFrom(from: string): Promise<FullCindexEntry[]> { const entries = ((await this.getOrNull(from)) || { issued: [], received: [] }).issued - return Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver', 'created_on']) + return Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver']) } trimExpiredCerts(belowNumber: number): Promise<void> { diff --git a/app/lib/dto/BlockDTO.ts b/app/lib/dto/BlockDTO.ts index 9ff80b246689d8e6f9211ccb565bbf8164c95b43..4a25a8f4fa0210ce705103d546dd09100ae6e2a4 100644 --- a/app/lib/dto/BlockDTO.ts +++ b/app/lib/dto/BlockDTO.ts @@ -280,7 +280,8 @@ export class BlockDTO implements Cloneable { udReevalTime0: parseInt(sp[18]), dtReeval: parseInt(sp[19]), // New parameter, defaults to msWindow - msPeriod: parseInt(sp[9]) + msPeriod: parseInt(sp[9]), + sigReplay: parseInt(sp[9]), } } diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts index b17a17ee6ca1e8dd4ea2ed05b64637beb0f3e3b7..3b8ed67ab22d6616b011df6093b4cd6f99e07411 100644 --- a/app/lib/dto/ConfDTO.ts +++ b/app/lib/dto/ConfDTO.ts @@ -44,6 +44,7 @@ export interface CurrencyConfDTO { dt: number ud0: number sigPeriod: number + sigReplay: number sigStock: number sigWindow: number sigValidity: number @@ -132,6 +133,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net public udReevalTime0: number, public stepMax: number, public sigPeriod: number, + public sigReplay: number, public msPeriod: number, public sigValidity: number, public msValidity: number, @@ -195,7 +197,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net ) {} static mock() { - return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true, false, 100, new ProxiesConf(), undefined) + return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, constants.CONTRACT.DEFAULT.SIGREPLAY, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true, false, 100, new ProxiesConf(), undefined) } static defaultConf() { @@ -211,6 +213,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net "ud0": constants.CONTRACT.DEFAULT.UD0, "stepMax": constants.CONTRACT.DEFAULT.STEPMAX, "sigPeriod": constants.CONTRACT.DEFAULT.SIGPERIOD, + "sigReplay": constants.CONTRACT.DEFAULT.SIGREPLAY, "sigValidity": constants.CONTRACT.DEFAULT.SIGVALIDITY, "msValidity": constants.CONTRACT.DEFAULT.MSVALIDITY, "sigQty": constants.CONTRACT.DEFAULT.SIGQTY, diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index c8bd03467407810b4e2c1898eaf792a5a817c3ea..8e600814331437b5341678d0a45fa94ddcd14c11 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -121,6 +121,7 @@ export interface CindexEntry extends IndexEntry { created_on: number, sig: string, chainable_on: number, + replayable_on: number, expires_on: number, expired_on: number, from_wid: null, // <-These 2 fields are useless @@ -133,6 +134,7 @@ export interface CindexEntry extends IndexEntry { toNewcomer?: boolean, toLeaver?: boolean, isReplay?: boolean, + isReplayable?: boolean, sigOK?: boolean, created_on_ref?: { medianTime: number }, } @@ -145,6 +147,7 @@ export interface FullCindexEntry { chainable_on: number expires_on: number expired_on: number + replayable_on: number } export interface SindexEntry extends IndexEntry { @@ -258,6 +261,7 @@ export class Indexer { msValidity:number, msPeriod:number, sigPeriod:number, + sigReplay:number, sigStock:number }): IndexEntry[] { @@ -473,6 +477,7 @@ export class Indexer { unchainables: 0, sig: cert.sig, chainable_on: block.medianTime + conf.sigPeriod, + replayable_on: block.medianTime + conf.sigReplay, expires_on: conf.sigValidity, expired_on: 0, from_wid: null, @@ -970,10 +975,17 @@ export class Indexer { ENTRY.toLeaver = !!(reduce(await dal.mindexDAL.reducable(ENTRY.receiver)).leaving) })) - // BR_G44 + // BR_G44 + 44.2 await Promise.all(cindex.map(async (ENTRY: CindexEntry) => { const reducable = await dal.cindexDAL.findByIssuerAndReceiver(ENTRY.issuer, ENTRY.receiver) ENTRY.isReplay = count(reducable) > 0 && reduce(reducable).expired_on === 0 + if (HEAD.number > 0 && HEAD_1.version > 10) { + ENTRY.isReplayable = count(reducable) === 0 || reduce(reducable).replayable_on < HEAD_1.medianTime + } + else { + // v10 blocks do not allow certification replay + ENTRY.isReplayable = false + } })) // BR_G45 @@ -1477,7 +1489,7 @@ export class Indexer { // BR_G71 static ruleCertificationReplay(cindex: CindexEntry[]) { for (const ENTRY of cindex) { - if (ENTRY.isReplay) return false; + if (ENTRY.isReplay && !ENTRY.isReplayable) return false } return true } diff --git a/app/modules/config.ts b/app/modules/config.ts index 5eee626cfdc28582cf8dd43e9e030251596b2c79..fe941d8c5f60d44644f86a6030bfaa119fdc5c05 100644 --- a/app/modules/config.ts +++ b/app/modules/config.ts @@ -29,6 +29,7 @@ module.exports = { config: { onLoading: async (conf:ConfDTO, program: ProgramOptions) => { conf.msPeriod = conf.msWindow + conf.sigReplay = conf.msPeriod conf.switchOnHeadAdvance = CommonConstants.SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS // Transactions storage @@ -43,6 +44,7 @@ module.exports = { }, beforeSave: async (conf:ConfDTO) => { conf.msPeriod = conf.msWindow + conf.sigReplay = conf.msPeriod conf.switchOnHeadAdvance = CommonConstants.SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS if (!conf.storage) { conf.storage = { diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts index dc592a5814a3c649fb93b7cf1f64c8ed13a5b03d..061fc384fc671403b650cfd193a937cb4934db41 100644 --- a/app/modules/prover/lib/blockGenerator.ts +++ b/app/modules/prover/lib/blockGenerator.ts @@ -410,7 +410,7 @@ export class BlockGenerator { } } // Already exists a link not replayable yet? - let exists = await this.dal.existsNonReplayableLink(cert.from, cert.to); + let exists = await this.dal.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version) if (exists) { throw 'It already exists a similar certification written, which is not replayable yet'; } @@ -756,7 +756,7 @@ class NextBlockGenerator implements BlockGeneratorInterface { let exists = false; if (current) { // Already exists a link not replayable yet? - exists = await this.dal.existsNonReplayableLink(cert.from, cert.to); + exists = await this.dal.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version) } if (!exists) { // Already exists a link not chainable yet? diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts index dba4089fc4d5d858dd36b3c26f9a743f3926c05a..b6de424cd0ef5f6ef1edfd3beda329a450111e07 100644 --- a/app/service/IdentityService.ts +++ b/app/service/IdentityService.ts @@ -227,7 +227,7 @@ export class IdentityService extends FIFOService { written_hash: null, block: cert.block_number } - let existingCert = await this.dal.existsCert(mCert); + let existingCert = await this.dal.existsCert(mCert, current) if (!existingCert) { if (!(await this.dal.certDAL.getSandboxForKey(cert.from).acceptNewSandBoxEntry(mCert, this.conf.pair && this.conf.pair.pub))) { throw constants.ERRORS.SANDBOX_FOR_CERT_IS_FULL; diff --git a/doc/Protocol.md b/doc/Protocol.md index ba7ac24c84811f70d1a64c0ded1737c613fc35af..6605ee6e54580d6cd1903bccf86da32618fc1f2b 100644 --- a/doc/Protocol.md +++ b/doc/Protocol.md @@ -1137,6 +1137,7 @@ udTime0 | Time of first UD. udReevalTime0 | Time of first reevaluation of the UD. sigPeriod | Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero. msPeriod | Minimum delay between 2 memberships of a same issuer, in seconds. Must be positive or zero. +sigReplay | Minimum delay between 2 certifications of a same issuer to a same receiver, in seconds. Equals to `msPeriod`. sigStock | Maximum quantity of active certifications made by member. sigWindow | Maximum delay a certification can wait before being expired for non-writing. sigValidity | Maximum age of an active signature (in seconds) @@ -1391,6 +1392,7 @@ Each certification produces 1 new entry: sig = SIGNATURE expires_on = MedianTime + sigValidity chainable_on = MedianTime + sigPeriod + replayable_on = MedianTime + sigReplay expired_on = 0 ) @@ -2274,6 +2276,25 @@ If `count(reducable) == 0`: Else: ENTRY.isReplay = reduce(reducable).expired_on == 0 + +####### BR_G44.2 - ENTRY.isReplayable + +If `HEAD.number > 0 && HEAD~1.version > 10` : + + reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0] + + If `count(reducable) == 0`: + + ENTRY.isReplayable = true + + Else: + + ENTRY.isReplayable = reduce(reducable).replayable_on < HEAD~1.medianTime +Else: + + ENTRY.isReplayable = false + +EndIf ####### BR_G45 - ENTRY.sigOK @@ -2519,7 +2540,7 @@ Rule: Rule: - ENTRY.isReplay == false + ENTRY.isReplay == false || ENTRY.isReplayable == true ###### BR_G72 - Certification signature diff --git a/test/fast/index/v1.0-local-index.ts b/test/fast/index/v1.0-local-index.ts index acfc7bcc05dba73aca70d214528795e3c42ca9d2..17a8fa7b29605c80b7817be4c8149216240167f0 100644 --- a/test/fast/index/v1.0-local-index.ts +++ b/test/fast/index/v1.0-local-index.ts @@ -103,7 +103,8 @@ describe("v1.0 Local Index", function(){ // We don't care about these in this test msPeriod: 0, sigPeriod: 0, - sigStock: 0 + sigStock: 0, + sigReplay: 0, }); }); diff --git a/test/integration/certification/certification-replay.ts b/test/integration/certification/certification-replay.ts new file mode 100644 index 0000000000000000000000000000000000000000..aee047c9103173bd192f79e4394c32b968b65ac0 --- /dev/null +++ b/test/integration/certification/certification-replay.ts @@ -0,0 +1,69 @@ +// 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 {assertThrows} from "../../unit-tools" +import {reduce} from "../../../app/lib/indexer" + +describe('Certification replay', () => writeBasicTestWithConfAnd2Users({ + sigReplay: 3, + sigPeriod: 0, + sigValidity: 10, +}, (test) => { + + const now = 1500000000 + + test('should be able to init with 2 blocks', async (s1, cat, tac) => { + await cat.createIdentity() + await tac.createIdentity() + await cat.cert(tac) + await tac.cert(cat) + await cat.join() + await tac.join() + await s1.commit({ time: now, version: 10 }) + await s1.commit({ time: now }) + }) + + test('should exist only 1 valid link from cat replyable at t + 3', async (s1, cat) => { + const reducableFromCat = await s1.dal.cindexDAL.reducablesFrom(cat.pub) + assertEqual(reducableFromCat.length, 1) + assertEqual(reduce(reducableFromCat).chainable_on, now) // No delay between certifications + assertEqual(reduce(reducableFromCat).replayable_on, now + 3) // Replay of a particular certification <-- What we want to test + assertEqual(reduce(reducableFromCat).expires_on, now + 10) // The expiration date of the certification **INITIALLY** + }) + + test('should reject a replay from cat', async (s1, cat, tac) => { + await assertThrows(cat.cert(tac), '{\n "ucode": 1004,\n "message": "Already up-to-date"\n}') + }) + + test('should accept replay if time has passed enought', async (s1, cat, tac) => { + await s1.commit({ time: now + 4 }) + await s1.commit({ time: now + 4 }) + await cat.cert(tac) + const b = await s1.commit({ time: now + 4 }) + assertEqual(b.certifications.length, 1) + }) + + test('should exist only 2 CINDEX entries from cat', async (s1, cat) => { + const validLinksFromCat = await s1.dal.cindexDAL.findByIssuer(cat.pub) + assertEqual(validLinksFromCat.length, 2) + }) + + test('should exist only 1 valid link from cat', async (s1, cat) => { + const reducableFromCat = await s1.dal.cindexDAL.getValidLinksFrom(cat.pub) + assertEqual(reducableFromCat.length, 1) + assertEqual(reduce(reducableFromCat).chainable_on, now + 4) + assertEqual(reduce(reducableFromCat).replayable_on, now + 4 + 3) // Replayable date should have changed! + assertEqual(reduce(reducableFromCat).expires_on, now + 4 + 10) // The expiration date should have changed! (this is the interest of a replay) + }) +})) diff --git a/test/integration/tools/test-framework.ts b/test/integration/tools/test-framework.ts index 325d6bab70581a10afc30a02977391b894a34cdf..9adff77e90dc53949d22d8b6f0b2b5bb969484e7 100644 --- a/test/integration/tools/test-framework.ts +++ b/test/integration/tools/test-framework.ts @@ -1,6 +1,7 @@ import {catUser, NewTestingServer, tacUser, TestingServer, tocUser} from "./toolbox" import {TestUser} from "./TestUser" import * as assert from 'assert' +import {Underscore} from "../../../app/lib/common-libs/underscore" export function writeBasicTestWith2Users(writeTests: ( test: ( @@ -9,16 +10,30 @@ export function writeBasicTestWith2Users(writeTests: ( ) => void ) => void) { + writeBasicTestWithConfAnd2Users({}, writeTests) +} + +export function writeBasicTestWithConfAnd2Users(conf: { + +}, writeTests: ( + test: ( + testTitle: string, + fn: (server: TestingServer, cat: TestUser, tac: TestUser, toc: TestUser) => Promise<void> + ) => void +) => void) { + let s1:TestingServer, cat:TestUser, tac:TestUser, toc:TestUser + const configuration = Underscore.extend({ + medianTimeBlocks: 1, + pair: { + pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', + sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' + } + }, conf) + before(async () => { - s1 = NewTestingServer({ - medianTimeBlocks: 1, - pair: { - pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', - sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' - } - }) + s1 = NewTestingServer(configuration) cat = catUser(s1) tac = tacUser(s1) toc = tocUser(s1)