Mise à jour de GitLab prévue ce samedi 23 octobre 2021 à partir de 9h00 CET

DuniterBlockchain.ts 23.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
// 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.

14
15
16
17
18
19
20
import {
  BasedAmount,
  FullIindexEntry,
  IindexEntry,
  IndexEntry,
  Indexer,
  MindexEntry,
21
  SimpleSindexEntryForWallet,
22
  SimpleUdEntryForWallet
23
} from "../indexer"
24
25
26
27
import {ConfDTO} from "../dto/ConfDTO"
import {BlockDTO} from "../dto/BlockDTO"
import {DBHead} from "../db/DBHead"
import {DBBlock} from "../db/DBBlock"
28
import {CHECK} from "../rules/index"
29
30
import {RevocationDTO} from "../dto/RevocationDTO"
import {IdentityDTO} from "../dto/IdentityDTO"
31
import {CertificationDTO} from "../dto/CertificationDTO"
32
import {MembershipDTO} from "../dto/MembershipDTO"
33
import {TransactionDTO} from "../dto/TransactionDTO"
34
import {CommonConstants} from "../common-libs/constants"
35
import {FileDAL} from "../dal/fileDAL"
Cédric Moreau's avatar
Cédric Moreau committed
36
import {NewLogger} from "../logger"
37
import {DBTx} from "../db/DBTx"
38
import {Underscore} from "../common-libs/underscore"
39
import {OtherConstants} from "../other_constants"
40
41
import {MonitorExecutionTime} from "../debug/MonitorExecutionTime"
import {WoTBInstance} from "../wot"
42

Cédric Moreau's avatar
Cédric Moreau committed
43
export class DuniterBlockchain {
44

45
  static async checkBlock(block:BlockDTO, withPoWAndSignature:boolean, conf: ConfDTO, dal:FileDAL) {
46
    const index = Indexer.localIndex(block, conf)
47
    if (withPoWAndSignature) {
48
      await CHECK.ASYNC.ALL_LOCAL(block, conf, index)
49
50
    }
    else {
51
      await CHECK.ASYNC.ALL_LOCAL_BUT_POW(block, conf, index)
52
    }
53
    const HEAD = await Indexer.completeGlobalScope(block, conf, index, dal);
54
    const HEAD_1 = await dal.bindexDAL.head(1);
55
56
57
58
    const mindex = Indexer.mindex(index);
    const iindex = Indexer.iindex(index);
    const sindex = Indexer.sindex(index);
    const cindex = Indexer.cindex(index);
59
    // BR_G49
60
    if (Indexer.ruleVersion(HEAD, HEAD_1) === false) throw Error('ruleVersion');
61
    // BR_G50
62
    if (Indexer.ruleBlockSize(HEAD) === false) throw Error('ruleBlockSize');
63
    // BR_G98
64
    if (Indexer.ruleCurrency(block, HEAD) === false) throw Error('ruleCurrency');
65
    // BR_G51
Cédric Moreau's avatar
Cédric Moreau committed
66
67
68
    if (Indexer.ruleNumber(block, HEAD) === false) {
      throw Error('ruleNumber')
    }
69
    // BR_G52
70
    if (Indexer.rulePreviousHash(block, HEAD) === false) throw Error('rulePreviousHash');
71
    // BR_G53
72
    if (Indexer.rulePreviousIssuer(block, HEAD) === false) throw Error('rulePreviousIssuer');
73
    // BR_G101
74
    if (Indexer.ruleIssuerIsMember(HEAD) === false) throw Error('ruleIssuerIsMember');
75
    // BR_G54
76
    if (Indexer.ruleIssuersCount(block, HEAD) === false) throw Error('ruleIssuersCount');
77
    // BR_G55
78
    if (Indexer.ruleIssuersFrame(block, HEAD) === false) throw Error('ruleIssuersFrame');
79
    // BR_G56
80
    if (Indexer.ruleIssuersFrameVar(block, HEAD) === false) throw Error('ruleIssuersFrameVar');
81
    // BR_G57
82
83
84
    if (Indexer.ruleMedianTime(block, HEAD) === false) {
      throw Error('ruleMedianTime')
    }
85
    // BR_G58
86
    if (Indexer.ruleDividend(block, HEAD) === false) throw Error('ruleDividend');
87
    // BR_G59
88
    if (Indexer.ruleUnitBase(block, HEAD) === false) throw Error('ruleUnitBase');
89
    // BR_G60
90
    if (Indexer.ruleMembersCount(block, HEAD) === false) throw Error('ruleMembersCount');
91
    // BR_G61
92
    if (Indexer.rulePowMin(block, HEAD) === false) throw Error('rulePowMin');
93
94
    if (withPoWAndSignature) {
      // BR_G62
95
      if (Indexer.ruleProofOfWork(HEAD) === false) throw Error('ruleProofOfWork');
96
97
    }
    // BR_G63
98
    if (Indexer.ruleIdentityWritability(iindex, conf) === false) throw Error('ruleIdentityWritability');
99
    // BR_G64
100
    if (Indexer.ruleMembershipWritability(mindex, conf) === false) throw Error('ruleMembershipWritability');
101
    // BR_G108
102
    if (Indexer.ruleMembershipPeriod(mindex) === false) throw Error('ruleMembershipPeriod');
103
    // BR_G65
104
    if (Indexer.ruleCertificationWritability(cindex, conf) === false) throw Error('ruleCertificationWritability');
105
    // BR_G66
106
    if (Indexer.ruleCertificationStock(cindex, conf) === false) throw Error('ruleCertificationStock');
107
    // BR_G67
108
    if (Indexer.ruleCertificationPeriod(cindex) === false) throw Error('ruleCertificationPeriod');
109
    // BR_G68
110
    if (Indexer.ruleCertificationFromMember(HEAD, cindex) === false) throw Error('ruleCertificationFromMember');
111
    // BR_G69
112
    if (Indexer.ruleCertificationToMemberOrNewcomer(cindex) === false) throw Error('ruleCertificationToMemberOrNewcomer');
113
    // BR_G70
114
    if (Indexer.ruleCertificationToLeaver(cindex) === false) throw Error('ruleCertificationToLeaver');
115
    // BR_G71
Cédric Moreau's avatar
Cédric Moreau committed
116
117
118
    if (Indexer.ruleCertificationReplay(cindex) === false) {
      throw Error('ruleCertificationReplay')
    }
119
    // BR_G72
120
    if (Indexer.ruleCertificationSignature(cindex) === false) throw Error('ruleCertificationSignature');
121
    // BR_G73
122
    if (Indexer.ruleIdentityUIDUnicity(iindex) === false) throw Error('ruleIdentityUIDUnicity');
123
    // BR_G74
124
    if (Indexer.ruleIdentityPubkeyUnicity(iindex) === false) throw Error('ruleIdentityPubkeyUnicity');
125
    // BR_G75
126
    if (Indexer.ruleMembershipSuccession(mindex) === false) throw Error('ruleMembershipSuccession');
127
    // BR_G76
128
    if (Indexer.ruleMembershipDistance(HEAD, mindex) === false) throw Error('ruleMembershipDistance');
129
    // BR_G77
130
    if (Indexer.ruleMembershipOnRevoked(mindex) === false) throw Error('ruleMembershipOnRevoked');
131
    // BR_G78
132
    if (Indexer.ruleMembershipJoinsTwice(mindex) === false) throw Error('ruleMembershipJoinsTwice');
133
    // BR_G79
134
    if (Indexer.ruleMembershipEnoughCerts(mindex) === false) throw Error('ruleMembershipEnoughCerts');
135
    // BR_G80
136
    if (Indexer.ruleMembershipLeaverIsMember(mindex) === false) throw Error('ruleMembershipLeaverIsMember');
137
    // BR_G81
Cédric Moreau's avatar
Cédric Moreau committed
138
139
140
    if (Indexer.ruleMembershipActiveIsMember(mindex) === false) {
      throw Error('ruleMembershipActiveIsMember')
    }
141
    // BR_G82
142
    if (Indexer.ruleMembershipRevokedIsMember(mindex) === false) throw Error('ruleMembershipRevokedIsMember');
143
    // BR_G83
144
    if (Indexer.ruleMembershipRevokedSingleton(mindex) === false) throw Error('ruleMembershipRevokedSingleton');
145
    // BR_G84
146
    if (Indexer.ruleMembershipRevocationSignature(mindex) === false) throw Error('ruleMembershipRevocationSignature');
147
    // BR_G85
148
    if (Indexer.ruleMembershipExcludedIsMember(iindex) === false) throw Error('ruleMembershipExcludedIsMember');
149
    // BR_G86
Cédric Moreau's avatar
Cédric Moreau committed
150
151
152
    if ((await Indexer.ruleToBeKickedArePresent(iindex, dal)) === false) {
      throw Error('ruleToBeKickedArePresent')
    }
153
    // BR_G103
154
    if (Indexer.ruleTxWritability(sindex) === false) throw Error('ruleTxWritability');
155
    // BR_G87
156
    if (Indexer.ruleInputIsAvailable(sindex) === false) throw Error('ruleInputIsAvailable');
157
    // BR_G88
158
    if (Indexer.ruleInputIsUnlocked(sindex) === false) throw Error('ruleInputIsUnlocked');
159
    // BR_G89
160
    if (Indexer.ruleInputIsTimeUnlocked(sindex) === false) throw Error('ruleInputIsTimeUnlocked');
161
    // BR_G90
162
    if (Indexer.ruleOutputBase(sindex, HEAD_1) === false) throw Error('ruleOutputBase');
163
164
    // Check document's coherence

165
    const matchesList = (regexp:RegExp, list:string[]) => {
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
      let i = 0;
      let found = "";
      while (!found && i < list.length) {
        found = list[i].match(regexp) ? list[i] : "";
        i++;
      }
      return found;
    }

    const isMember = await dal.isMember(block.issuer);
    if (!isMember) {
      if (block.number == 0) {
        if (!matchesList(new RegExp('^' + block.issuer + ':'), block.joiners)) {
          throw Error('Block not signed by the root members');
        }
      } else {
        throw Error('Block must be signed by an existing member');
      }
    }

    // Generate the local index
    // Check the local rules
    // Enrich with the global index
    // Check the global rules
    return { index, HEAD }
  }

Cédric Moreau's avatar
Cédric Moreau committed
193
  static async pushTheBlock(obj:BlockDTO, index:IndexEntry[], HEAD:DBHead | null, conf:ConfDTO, dal:FileDAL, logger:any, trim = true) {
194
    const start = Date.now();
195
    const block = BlockDTO.fromJSONObject(obj)
196
197
198
    try {
      const currentBlock = await dal.getCurrentBlockOrNull();
      block.fork = false;
199
      const added = await this.saveBlockData(currentBlock, block, conf, dal, logger, index, HEAD, trim);
200
201
202
203
204
205
206
207

      try {
        await DuniterBlockchain.pushStatsForBlocks([block], dal);
      } catch (e) {
        logger.warn("An error occurred after the add of the block", e.stack || e);
      }

      logger.info('Block #' + block.number + ' added to the blockchain in %s ms', (Date.now() - start));
208

209
      return BlockDTO.fromJSONObject(added)
210
211
212
213
214
215
216
217
218
219
220
    }
    catch(err) {
      throw err;
    }

    // Enrich the index with post-HEAD indexes
    // Push the block into the blockchain
    // await supra.pushBlock(b)
    // await supra.recordIndex(index)
  }

Cédric Moreau's avatar
Cédric Moreau committed
221
  static async saveBlockData(current:DBBlock|null, block:BlockDTO, conf:ConfDTO, dal:FileDAL, logger:any, index:IndexEntry[], HEAD:DBHead | null, trim: boolean) {
222
223
224
225
226
227
228
229
230
231
    if (block.number == 0) {
      await this.saveParametersForRoot(block, conf, dal);
    }

    const indexes = await dal.generateIndexes(block, conf, index, HEAD);

    // Newcomers
    await this.createNewcomers(indexes.iindex, dal, logger);

    // Save indexes
Cédric Moreau's avatar
Cédric Moreau committed
232
    await dal.bindexDAL.insert(indexes.HEAD);
233
    await dal.flushIndexes(indexes)
234
235
236
237
238

    // Create/Update nodes in wotb
    await this.updateMembers(block, dal);

    // Update the wallets' blances
239
    await this.updateWallets(indexes.sindex, indexes.dividends, dal)
240

241
    if (trim) {
242
      await DuniterBlockchain.trimIndexes(dal, indexes.HEAD, conf)
243
244
    }

245
    const dbb = DBBlock.fromBlockDTO(block)
246
    this.updateBlocksComputedVars(current, dbb)
247
248
249
250
251

    // --> Update links
    await dal.updateWotbLinks(indexes.cindex);

    // Create/Update certifications
252
    await DuniterBlockchain.removeCertificationsFromSandbox(block, dal);
253
254
255
256
257
258
259
260
    // Create/Update memberships
    await this.removeMembershipsFromSandbox(block, dal);
    // Compute to be revoked members
    await this.computeToBeRevoked(indexes.mindex, dal);
    // Delete eventually present transactions
    await this.deleteTransactions(block, dal);

    await dal.trimSandboxes(block);
261
262
    // Saves the block (DAL)
    await dal.saveBlock(dbb);
263

264
    return dbb
265
266
  }

Cédric Moreau's avatar
Cédric Moreau committed
267
  static async saveParametersForRoot(block:BlockDTO, conf:ConfDTO, dal:FileDAL) {
268
    if (block.parameters) {
269
      const bconf = BlockDTO.getConf(block)
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
      conf.c = bconf.c;
      conf.dt = bconf.dt;
      conf.ud0 = bconf.ud0;
      conf.sigPeriod = bconf.sigPeriod;
      conf.sigStock = bconf.sigStock;
      conf.sigWindow = bconf.sigWindow;
      conf.sigValidity = bconf.sigValidity;
      conf.sigQty = bconf.sigQty;
      conf.idtyWindow = bconf.idtyWindow;
      conf.msWindow = bconf.msWindow;
      conf.xpercent = bconf.xpercent;
      conf.msValidity = bconf.msValidity;
      conf.stepMax = bconf.stepMax;
      conf.medianTimeBlocks = bconf.medianTimeBlocks;
      conf.avgGenTime = bconf.avgGenTime;
      conf.dtDiffEval = bconf.dtDiffEval;
      conf.percentRot = bconf.percentRot;
      conf.udTime0 = bconf.udTime0;
      conf.udReevalTime0 = bconf.udReevalTime0;
      conf.dtReeval = bconf.dtReeval;
290
      conf.currency = bconf.currency;
291
292
293
294
295
296
      // Super important: adapt wotb module to handle the correct stock
      dal.wotb.setMaxCert(conf.sigStock);
      return dal.saveConf(conf);
    }
  }

297
298
299
  @MonitorExecutionTime()
  static async createNewcomers(iindex:IindexEntry[], dal:FileDAL, logger:any, instance?: WoTBInstance) {
    const wotb = instance || dal.wotb
300
301
302
    for (const i of iindex) {
      if (i.op == CommonConstants.IDX_CREATE) {
        const entry = i as FullIindexEntry
303
        // Reserves a wotb ID
304
        entry.wotb_id = wotb.addNode();
305
306
        logger.trace('%s was affected wotb_id %s', entry.uid, entry.wotb_id);
        // Remove from the sandbox any other identity with the same pubkey/uid, since it has now been reserved.
307
308
        await dal.removeUnWrittenWithPubkey(entry.pub)
        await dal.removeUnWrittenWithUID(entry.uid)
309
310
311
312
      }
    }
  }

313
314
  static async updateMembers(block:BlockDTO, dal:FileDAL, instance?: WoTBInstance) {
    const wotb = instance || dal.wotb
315
316
    // Joiners (come back)
    for (const inlineMS of block.joiners) {
317
      let ms = MembershipDTO.fromInline(inlineMS)
318
      const idty = await dal.getWrittenIdtyByPubkeyForWotbID(ms.issuer);
319
      wotb.setEnabled(true, idty.wotb_id);
320
      await dal.dividendDAL.setMember(true, ms.issuer)
321
322
323
    }
    // Revoked
    for (const inlineRevocation of block.revoked) {
324
      let revocation = RevocationDTO.fromInline(inlineRevocation)
325
      await dal.revokeIdentity(revocation.pubkey)
326
327
328
    }
    // Excluded
    for (const excluded of block.excluded) {
329
      const idty = await dal.getWrittenIdtyByPubkeyForWotbID(excluded);
330
      wotb.setEnabled(false, idty.wotb_id);
331
      await dal.dividendDAL.setMember(false, excluded)
332
333
334
    }
  }

335
  static async updateWallets(sindex:SimpleSindexEntryForWallet[], dividends:SimpleUdEntryForWallet[], aDal:any, reverse = false, at?: number) {
336
    const differentConditions = Underscore.uniq(sindex.map((entry) => entry.conditions).concat(dividends.map(d => d.conditions)))
337
    for (const conditions of differentConditions) {
338
339
340
341
342
      const udsOfKey: BasedAmount[] = dividends.filter(d => d.conditions === conditions).map(d => ({ amount: d.amount, base: d.base }))
      const creates: BasedAmount[] = sindex.filter(entry => entry.conditions === conditions && entry.op === CommonConstants.IDX_CREATE)
      const updates: BasedAmount[] = sindex.filter(entry => entry.conditions === conditions && entry.op === CommonConstants.IDX_UPDATE)
      const positives = creates.concat(udsOfKey).reduce((sum, src) => sum + src.amount * Math.pow(10, src.base), 0)
      const negatives = updates.reduce((sum, src) => sum + src.amount * Math.pow(10, src.base), 0)
343
344
345
346
347
348
      const wallet = await aDal.getWallet(conditions)
      let variation = positives - negatives
      if (reverse) {
        // To do the opposite operations, for a reverted block
        variation *= -1
      }
349
      if (OtherConstants.TRACE_BALANCES) {
350
351
352
        if (!OtherConstants.TRACE_PARTICULAR_BALANCE || wallet.conditions.match(new RegExp(OtherConstants.TRACE_PARTICULAR_BALANCE))) {
          NewLogger().trace('Balance of %s: %s (%s %s %s) at #%s', wallet.conditions, wallet.balance + variation, wallet.balance, variation < 0 ? '-' : '+', Math.abs(variation), at)
        }
353
      }
354
      wallet.balance += variation
355
356
357
      if (OtherConstants.TRACE_PARTICULAR_BALANCE && wallet.conditions.match(new RegExp(OtherConstants.TRACE_PARTICULAR_BALANCE))) {
        NewLogger().trace('>>>>>>>>> WALLET = ', (wallet.balance > 0 ? '+' : '') + wallet.balance)
      }
358
359
360
361
      await aDal.saveWallet(wallet)
    }
  }

362
  static async revertBlock(number:number, hash:string, dal:FileDAL, block?: DBBlock) {
363
364
365
366
367
368

    const blockstamp = [number, hash].join('-');

    // Revert links
    const writtenOn = await dal.cindexDAL.getWrittenOn(blockstamp);
    for (const entry of writtenOn) {
369
370
      const from = await dal.getWrittenIdtyByPubkeyForWotbID(entry.issuer);
      const to = await dal.getWrittenIdtyByPubkeyForWotbID(entry.receiver);
371
      if (entry.op == CommonConstants.IDX_CREATE) {
372
        // We remove the created link
373
        dal.wotb.removeLink(from.wotb_id, to.wotb_id);
374
375
      } else {
        // We add the removed link
376
        dal.wotb.addLink(from.wotb_id, to.wotb_id);
377
378
379
380
381
382
383
384
      }
    }

    // Revert nodes
    await this.undoMembersUpdate(blockstamp, dal);

    // Get the money movements to revert in the balance
    const REVERSE_BALANCE = true
385
    const sindexOfBlock = await dal.sindexDAL.getWrittenOnTxs(blockstamp)
386

Cédric Moreau's avatar
Cédric Moreau committed
387
    await dal.bindexDAL.removeBlock(blockstamp);
388
389
390
391
    await dal.mindexDAL.removeBlock(blockstamp);
    await dal.iindexDAL.removeBlock(blockstamp);
    await dal.cindexDAL.removeBlock(blockstamp);
    await dal.sindexDAL.removeBlock(blockstamp);
392
    const { createdUDsDestroyedByRevert, consumedUDsRecoveredByRevert } = await dal.dividendDAL.revertUDs(number)
393
394

    // Then: normal updates
395
    const previousBlock = await dal.getFullBlockOf(number - 1)
396
397
398
    // Set the block as SIDE block (equivalent to removal from main branch)
    await dal.blockDAL.setSideBlock(number, previousBlock);

399
400
401
    // Update the dividends in our wallet
    await this.updateWallets([], createdUDsDestroyedByRevert, dal, REVERSE_BALANCE)
    await this.updateWallets([], consumedUDsRecoveredByRevert, dal)
402
    // Revert the balances variations for this block
403
    await this.updateWallets(sindexOfBlock, [], dal, REVERSE_BALANCE)
404
405

    // Restore block's transaction as incoming transactions
406
407
408
    if (block) {
      await this.undoDeleteTransactions(block, dal)
    }
409
410
  }

Cédric Moreau's avatar
Cédric Moreau committed
411
  static async undoMembersUpdate(blockstamp:string, dal:FileDAL) {
412
413
414
415
    const joiners = await dal.iindexDAL.getWrittenOn(blockstamp);
    for (const entry of joiners) {
      // Undo 'join' which can be either newcomers or comebackers
      // => equivalent to i_index.member = true AND i_index.op = 'UPDATE'
416
      if (entry.member === true && entry.op === CommonConstants.IDX_UPDATE) {
417
        const idty = await dal.getWrittenIdtyByPubkeyForWotbID(entry.pub);
418
        dal.wotb.setEnabled(false, idty.wotb_id);
419
        await dal.dividendDAL.setMember(false, entry.pub)
420
421
422
423
424
425
      }
    }
    const newcomers = await dal.iindexDAL.getWrittenOn(blockstamp);
    for (const entry of newcomers) {
      // Undo newcomers
      // => equivalent to i_index.op = 'CREATE'
426
      if (entry.op === CommonConstants.IDX_CREATE) {
427
        // Does not matter which one it really was, we pop the last X identities
Cédric Moreau's avatar
Cédric Moreau committed
428
        NewLogger().trace('removeNode')
429
        dal.wotb.removeNode();
430
        await dal.dividendDAL.deleteMember(entry.pub)
431
432
433
434
435
436
      }
    }
    const excluded = await dal.iindexDAL.getWrittenOn(blockstamp);
    for (const entry of excluded) {
      // Undo excluded (make them become members again in wotb)
      // => equivalent to m_index.member = false
437
      if (entry.member === false && entry.op === CommonConstants.IDX_UPDATE) {
438
        const idty = await dal.getWrittenIdtyByPubkeyForWotbID(entry.pub);
439
        dal.wotb.setEnabled(true, idty.wotb_id);
440
        await dal.dividendDAL.setMember(true, entry.pub)
441
442
443
444
      }
    }
  }

Cédric Moreau's avatar
Cédric Moreau committed
445
  static async undoDeleteTransactions(block:DBBlock, dal:FileDAL) {
446
447
    for (const obj of block.transactions) {
      obj.currency = block.currency;
448
      let tx = TransactionDTO.fromJSONObject(obj)
449
      await dal.saveTransaction(DBTx.fromTransactionDTO(tx))
450
451
452
453
454
455
456
457
458
    }
  }

  /**
   * Delete certifications from the sandbox since it has been written.
   *
   * @param block Block in which are contained the certifications to remove from sandbox.
   * @param dal The DAL
   */
459
  static async removeCertificationsFromSandbox(block:BlockDTO, dal:FileDAL) {
460
    for (let inlineCert of block.certifications) {
461
      let cert = CertificationDTO.fromInline(inlineCert)
462
      let idty = await dal.getWrittenIdtyByPubkeyForHashing(cert.to);
463
464
465
466
467
      await dal.deleteCert({
        from: cert.from,
        target: IdentityDTO.getTargetHash(idty),
        sig: cert.sig,
      });
468
469
470
471
472
473
474
475
476
    }
  }

  /**
   * Delete memberships from the sandbox since it has been written.
   *
   * @param block Block in which are contained the certifications to remove from sandbox.
   * @param dal The DAL
   */
Cédric Moreau's avatar
Cédric Moreau committed
477
  static async removeMembershipsFromSandbox(block:BlockDTO, dal:FileDAL) {
478
479
    const mss = block.joiners.concat(block.actives).concat(block.leavers);
    for (const inlineMS of mss) {
480
481
482
483
484
      let ms = MembershipDTO.fromInline(inlineMS)
      await dal.deleteMS({
        issuer: ms.issuer,
        signature: ms.signature
      });
485
486
487
    }
  }

Cédric Moreau's avatar
Cédric Moreau committed
488
  static async computeToBeRevoked(mindex:MindexEntry[], dal:FileDAL) {
489
    const revocations = Underscore.filter(mindex, (entry:MindexEntry) => !!(entry.revoked_on))
490
    for (const revoked of revocations) {
491
      await dal.setRevoked(revoked.pub)
492
493
494
    }
  }

Cédric Moreau's avatar
Cédric Moreau committed
495
  static async deleteTransactions(block:BlockDTO, dal:FileDAL) {
496
497
    for (const obj of block.transactions) {
      obj.currency = block.currency;
498
      const tx = TransactionDTO.fromJSONObject(obj)
499
500
501
502
503
      const txHash = tx.getHash();
      await dal.removeTxByHash(txHash);
    }
  }

Cédric Moreau's avatar
Cédric Moreau committed
504
  static updateBlocksComputedVars(
505
506
    current:{ unitbase:number, monetaryMass:number }|null,
    block:{ number:number, unitbase:number, dividend:number|null, membersCount:number, monetaryMass:number }): void {
507
508
509
510
511
512
    // Unit Base
    block.unitbase = (block.dividend && block.unitbase) || (current && current.unitbase) || 0;
    // Monetary Mass update
    if (current) {
      block.monetaryMass = (current.monetaryMass || 0)
        + (block.dividend || 0) * Math.pow(10, block.unitbase || 0) * block.membersCount;
513
514
    } else {
      block.monetaryMass = 0
515
516
517
518
519
520
521
522
523
524
    }
    // UD Time update
    if (block.number == 0) {
      block.dividend = null;
    }
    else if (!block.dividend) {
      block.dividend = null;
    }
  }

525
  static pushStatsForBlocks(blocks:BlockDTO[], dal:FileDAL) {
526
    const stats: { [k:string]: { blocks: number[], lastParsedBlock:number }} = {};
527
528
    // Stats
    for (const block of blocks) {
529
530
531
532
533
534
535
536
537
538
539
540
541
542
      const values = [
        { name: 'newcomers', value: block.identities },
        { name: 'certs',     value: block.certifications },
        { name: 'joiners',   value: block.joiners },
        { name: 'actives',   value: block.actives },
        { name: 'leavers',   value: block.leavers },
        { name: 'revoked',   value: block.revoked },
        { name: 'excluded',  value: block.excluded },
        { name: 'ud',        value: block.dividend },
        { name: 'tx',        value: block.transactions }
      ]
      for (const element of values) {
        if (!stats[element.name]) {
          stats[element.name] = { blocks: [], lastParsedBlock: -1 };
543
        }
544
545
546
        const stat = stats[element.name]
        const isPositiveValue = element.value && typeof element.value != 'object';
        const isNonEmptyArray = element.value && typeof element.value == 'object' && element.value.length > 0;
547
548
549
550
551
552
553
554
555
        if (isPositiveValue || isNonEmptyArray) {
          stat.blocks.push(block.number);
        }
        stat.lastParsedBlock = block.number;
      }
    }
    return dal.pushStats(stats);
  }

Cédric Moreau's avatar
Cédric Moreau committed
556
  static async pushSideBlock(obj:BlockDTO, dal:FileDAL, logger:any) {
557
    const start = Date.now();
558
    const block = DBBlock.fromBlockDTO(BlockDTO.fromJSONObject(obj))
559
560
561
562
563
    block.fork = true;
    try {
      // Saves the block (DAL)
      block.wrong = false;
      await dal.saveSideBlockInFile(block);
564
      logger.info('SIDE Block #%s-%s added to the blockchain in %s ms', block.number, block.hash.substr(0, 8), (Date.now() - start));
565
566
567
568
569
      return block;
    } catch (err) {
      throw err;
    }
  }
570
571
572
573
574
575
576
577
578

  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);
    }
  }
579
}
580
581
582
583
584
585
586
587
588
589
590
591

export function requiredBindexSizeForTail(TAIL: { issuersCount: number, issuersFrame: number }, conf: { medianTimeBlocks: number, dtDiffEval: number, forksize: number }) {
  const bindexSize = [
    TAIL.issuersCount,
    TAIL.issuersFrame,
    conf.medianTimeBlocks,
    conf.dtDiffEval
  ].reduce((max, value) => {
    return Math.max(max, value);
  }, 0);
  return conf.forksize + bindexSize
}