From 45b95588a3f568a04c1f029f349fcd7ed250dd97 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Moreau?= <cem.moreau@gmail.com>
Date: Sun, 10 Jun 2018 18:30:45 +0200
Subject: [PATCH] [enh] dividends are now stored in a specific loki collection

---
 app/lib/blockchain/DuniterBlockchain.ts       |  44 +++-
 app/lib/common-libs/errors.ts                 |   3 +
 app/lib/computation/QuickSync.ts              |  12 +-
 app/lib/dal/fileDAL.ts                        |  86 +++++--
 app/lib/dal/indexDAL/abstract/DividendDAO.ts  |  47 ++++
 app/lib/dal/indexDAL/abstract/IIndexDAO.ts    |   2 -
 app/lib/dal/indexDAL/abstract/SIndexDAO.ts    |  23 +-
 app/lib/dal/indexDAL/loki/LokiBIndex.ts       |   4 +-
 app/lib/dal/indexDAL/loki/LokiBlockchain.ts   |  12 +-
 app/lib/dal/indexDAL/loki/LokiCIndex.ts       |   4 +-
 .../indexDAL/loki/LokiCollectionManager.ts    |   2 +-
 app/lib/dal/indexDAL/loki/LokiDividend.ts     | 243 ++++++++++++++++++
 app/lib/dal/indexDAL/loki/LokiIIndex.ts       |   6 -
 app/lib/dal/indexDAL/loki/LokiIndex.ts        |  20 +-
 .../dal/indexDAL/loki/LokiProtocolIndex.ts    |  23 ++
 .../indexDAL/loki/LokiPubkeySharingIndex.ts   |   3 +-
 app/lib/dal/indexDAL/loki/LokiSIndex.ts       |  58 +++--
 app/lib/dal/indexDAL/loki/LokiTransactions.ts |   3 +-
 app/lib/dto/BlockDTO.ts                       |   2 +-
 app/lib/dto/TransactionDTO.ts                 |   4 +-
 app/lib/indexer.ts                            | 109 +++++---
 app/lib/other_constants.ts                    |   1 +
 app/lib/rules/global_rules.ts                 |   4 +-
 .../bma/lib/controllers/transactions.ts       |  16 +-
 test/dal/sources-dal.ts                       |   8 +-
 test/fast/dal/basic-loki.ts                   |   3 +-
 test/fast/protocol/protocol-brg106-number.ts  |  10 +-
 test/integration/branches/branches_revert2.ts |  43 +++-
 28 files changed, 627 insertions(+), 168 deletions(-)
 create mode 100644 app/lib/dal/indexDAL/abstract/DividendDAO.ts
 create mode 100644 app/lib/dal/indexDAL/loki/LokiDividend.ts
 create mode 100644 app/lib/dal/indexDAL/loki/LokiProtocolIndex.ts

diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts
index 9b2c004ab..a12d9cbae 100644
--- a/app/lib/blockchain/DuniterBlockchain.ts
+++ b/app/lib/blockchain/DuniterBlockchain.ts
@@ -11,7 +11,16 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 // GNU Affero General Public License for more details.
 
-import {FullIindexEntry, IindexEntry, IndexEntry, Indexer, MindexEntry, SindexEntry} from "../indexer"
+import {
+  BasedAmount,
+  FullIindexEntry,
+  IindexEntry,
+  IndexEntry,
+  Indexer,
+  MindexEntry,
+  SimpleSindexEntryForWallet, SimpleTxEntryForWallet, SimpleUdEntryForWallet,
+  SindexEntry
+} from "../indexer"
 import {ConfDTO} from "../dto/ConfDTO"
 import {BlockDTO} from "../dto/BlockDTO"
 import {DBHead} from "../db/DBHead"
@@ -28,6 +37,8 @@ import {DataErrors} from "../common-libs/errors"
 import {NewLogger} from "../logger"
 import {DBTx} from "../db/DBTx"
 import {Underscore} from "../common-libs/underscore"
+import {DividendEntry, UDSource} from "../dal/indexDAL/abstract/DividendDAO"
+import {OtherConstants} from "../other_constants"
 
 export class DuniterBlockchain {
 
@@ -224,7 +235,7 @@ export class DuniterBlockchain {
     await this.updateMembers(block, dal);
 
     // Update the wallets' blances
-    await this.updateWallets(indexes.sindex, dal)
+    await this.updateWallets(indexes.sindex, indexes.dividends, dal)
 
     if (trim) {
       const TAIL = await dal.bindexDAL.tail();
@@ -318,6 +329,7 @@ export class DuniterBlockchain {
       let ms = MembershipDTO.fromInline(inlineMS)
       const idty = await dal.getWrittenIdtyByPubkeyForWotbID(ms.issuer);
       dal.wotb.setEnabled(true, idty.wotb_id);
+      await dal.dividendDAL.setMember(true, ms.issuer)
     }
     // Revoked
     for (const inlineRevocation of block.revoked) {
@@ -328,22 +340,27 @@ export class DuniterBlockchain {
     for (const excluded of block.excluded) {
       const idty = await dal.getWrittenIdtyByPubkeyForWotbID(excluded);
       dal.wotb.setEnabled(false, idty.wotb_id);
+      await dal.dividendDAL.setMember(false, excluded)
     }
   }
 
-  static async updateWallets(sindex:SindexEntry[], aDal:any, reverse = false) {
-    const differentConditions = Underscore.uniq(sindex.map((entry) => entry.conditions))
+  static async updateWallets(sindex:SimpleTxEntryForWallet[], dividends:SimpleUdEntryForWallet[], aDal:any, reverse = false) {
+    const differentConditions = Underscore.uniq(sindex.map((entry) => entry.conditions).concat(dividends.map(d => d.conditions)))
     for (const conditions of differentConditions) {
-      const creates = Underscore.filter(sindex, (entry:SindexEntry) => entry.conditions === conditions && entry.op === CommonConstants.IDX_CREATE)
-      const updates = Underscore.filter(sindex, (entry:SindexEntry) => entry.conditions === conditions && entry.op === CommonConstants.IDX_UPDATE)
-      const positives = creates.reduce((sum:number, src:SindexEntry) => sum + src.amount * Math.pow(10, src.base), 0)
-      const negatives = updates.reduce((sum:number, src:SindexEntry) => sum + src.amount * Math.pow(10, src.base), 0)
+      const 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)
       const wallet = await aDal.getWallet(conditions)
       let variation = positives - negatives
       if (reverse) {
         // To do the opposite operations, for a reverted block
         variation *= -1
       }
+      if (OtherConstants.TRACE_BALANCES) {
+        NewLogger().trace('Balance of %s: %s (%s %s %s)', wallet.conditions, wallet.balance + variation, wallet.balance, variation < 0 ? '-' : '+', Math.abs(variation))
+      }
       wallet.balance += variation
       await aDal.saveWallet(wallet)
     }
@@ -377,21 +394,25 @@ export class DuniterBlockchain {
 
     // Get the money movements to revert in the balance
     const REVERSE_BALANCE = true
-    const sindexOfBlock = await dal.sindexDAL.getWrittenOn(blockstamp)
+    const sindexOfBlock = await dal.sindexDAL.getWrittenOnTxs(blockstamp)
 
     await dal.bindexDAL.removeBlock(blockstamp);
     await dal.mindexDAL.removeBlock(blockstamp);
     await dal.iindexDAL.removeBlock(blockstamp);
     await dal.cindexDAL.removeBlock(blockstamp);
     await dal.sindexDAL.removeBlock(blockstamp);
+    const { createdUDsDestroyedByRevert, consumedUDsRecoveredByRevert } = await dal.dividendDAL.revertUDs(number)
 
     // Then: normal updates
     const previousBlock = await dal.getFullBlockOf(number - 1)
     // Set the block as SIDE block (equivalent to removal from main branch)
     await dal.blockDAL.setSideBlock(number, previousBlock);
 
+    // Update the dividends in our wallet
+    await this.updateWallets([], createdUDsDestroyedByRevert, dal, REVERSE_BALANCE)
+    await this.updateWallets([], consumedUDsRecoveredByRevert, dal)
     // Revert the balances variations for this block
-    await this.updateWallets(sindexOfBlock, dal, REVERSE_BALANCE)
+    await this.updateWallets(sindexOfBlock, [], dal, REVERSE_BALANCE)
 
     // Restore block's transaction as incoming transactions
     await this.undoDeleteTransactions(block, dal)
@@ -407,6 +428,7 @@ export class DuniterBlockchain {
       if (entry.member === true && entry.op === CommonConstants.IDX_UPDATE) {
         const idty = await dal.getWrittenIdtyByPubkeyForWotbID(entry.pub);
         dal.wotb.setEnabled(false, idty.wotb_id);
+        await dal.dividendDAL.setMember(false, entry.pub)
       }
     }
     const newcomers = await dal.iindexDAL.getWrittenOn(blockstamp);
@@ -417,6 +439,7 @@ export class DuniterBlockchain {
         // Does not matter which one it really was, we pop the last X identities
         NewLogger().trace('removeNode')
         dal.wotb.removeNode();
+        await dal.dividendDAL.deleteMember(entry.pub)
       }
     }
     const excluded = await dal.iindexDAL.getWrittenOn(blockstamp);
@@ -426,6 +449,7 @@ export class DuniterBlockchain {
       if (entry.member === false && entry.op === CommonConstants.IDX_UPDATE) {
         const idty = await dal.getWrittenIdtyByPubkeyForWotbID(entry.pub);
         dal.wotb.setEnabled(true, idty.wotb_id);
+        await dal.dividendDAL.setMember(true, entry.pub)
       }
     }
   }
diff --git a/app/lib/common-libs/errors.ts b/app/lib/common-libs/errors.ts
index 2af726625..74d0c31b3 100644
--- a/app/lib/common-libs/errors.ts
+++ b/app/lib/common-libs/errors.ts
@@ -1,5 +1,8 @@
 
 export enum DataErrors {
+  LOKI_DIVIDEND_GET_WRITTEN_ON_SHOULD_NOT_BE_USED,
+  LOKI_DIVIDEND_REMOVE_BLOCK_SHOULD_NOT_BE_USED,
+  NEGATIVE_BALANCE,
   BLOCK_WASNT_COMMITTED,
   CANNOT_ARCHIVE_CHUNK_WRONG_SIZE,
   CORRUPTED_DATABASE,
diff --git a/app/lib/computation/QuickSync.ts b/app/lib/computation/QuickSync.ts
index 4993cbbe4..b7f4cd5f6 100644
--- a/app/lib/computation/QuickSync.ts
+++ b/app/lib/computation/QuickSync.ts
@@ -166,9 +166,9 @@ export class QuickSynchronizer {
           sync_nextExpiring = sync_expires.reduce((max, value) => max ? Math.min(max, value) : value, 9007199254740991); // Far far away date
 
           // Fills in correctly the SINDEX
-          await Promise.all(Underscore.where(sync_sindex.concat(local_sindex), { op: 'UPDATE' }).map(async (entry: any) => {
+          await Promise.all(Underscore.where(sync_sindex.concat(local_sindex), { op: 'UPDATE' }).map(async entry => {
             if (!entry.conditions) {
-              const src = (await this.dal.sindexDAL.getSource(entry.identifier, entry.pos)) as FullSindexEntry
+              const src = (await this.dal.getSource(entry.identifier, entry.pos, entry.srcType === 'D')) as FullSindexEntry
               entry.conditions = src.conditions;
             }
           }))
@@ -185,8 +185,10 @@ export class QuickSynchronizer {
           sync_mindex = local_mindex
           sync_sindex = local_sindex
 
-          sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGenDividend(HEAD, local_iindex, this.dal));
-          sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGarbageSmallAccounts(HEAD, sync_sindex, sync_memoryDAL));
+          // Dividends and account garbaging
+          const dividends = await Indexer.ruleIndexGenDividend(HEAD, local_iindex, this.dal)
+          sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGarbageSmallAccounts(HEAD, sync_sindex, dividends, sync_memoryDAL));
+
           if (nextExpiringChanged) {
             sync_cindex = sync_cindex.concat(await Indexer.ruleIndexGenCertificationExpiry(HEAD, this.dal));
             sync_mindex = sync_mindex.concat(await Indexer.ruleIndexGenMembershipExpiry(HEAD, this.dal));
@@ -195,7 +197,7 @@ export class QuickSynchronizer {
             sync_mindex = sync_mindex.concat(await Indexer.ruleIndexGenImplicitRevocation(HEAD, this.dal));
           }
           // Update balances with UD + local garbagings
-          await DuniterBlockchain.updateWallets(sync_sindex, sync_memoryDAL)
+          await DuniterBlockchain.updateWallets(sync_sindex, dividends, sync_memoryDAL)
 
           // Flush the INDEX again (needs to be done *before* the update of wotb links because of block#0)
           await this.dal.flushIndexes({
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index 12a07818d..7b0586869 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -25,10 +25,11 @@ import {
   FullCindexEntry,
   FullIindexEntry,
   FullMindexEntry,
-  FullSindexEntry,
   IindexEntry,
   IndexEntry,
   MindexEntry,
+  SimpleTxInput,
+  SimpleUdEntryForWallet,
   SindexEntry
 } from "../indexer"
 import {TransactionDTO} from "../dto/TransactionDTO"
@@ -75,6 +76,9 @@ import {Underscore} from "../common-libs/underscore"
 import {DBPeer} from "../db/DBPeer"
 import {MonitorFlushedIndex} from "../debug/MonitorFlushedIndex"
 import {cliprogram} from "../common-libs/programOptions"
+import {DividendDAO, UDSource} from "./indexDAL/abstract/DividendDAO"
+import {LokiDividend} from "./indexDAL/loki/LokiDividend"
+import {HttpSource, HttpUD} from "../../modules/bma/lib/dtos"
 
 const readline = require('readline')
 const indexer = require('../indexer').Indexer
@@ -126,6 +130,7 @@ export class FileDAL {
   iindexDAL:IIndexDAO
   sindexDAL:SIndexDAO
   cindexDAL:CIndexDAO
+  dividendDAL:DividendDAO
   newDals:{ [k:string]: Initiable }
 
   loadConfHook: (conf:ConfDTO) => Promise<void>
@@ -156,6 +161,7 @@ export class FileDAL {
     this.iindexDAL = new LokiIIndex(this.loki.getLokiInstance())
     this.sindexDAL = new LokiSIndex(this.loki.getLokiInstance())
     this.cindexDAL = new LokiCIndex(this.loki.getLokiInstance())
+    this.dividendDAL = new LokiDividend(this.loki.getLokiInstance())
 
     this.newDals = {
       'powDAL': this.powDAL,
@@ -174,6 +180,7 @@ export class FileDAL {
       'iindexDAL': this.iindexDAL,
       'sindexDAL': this.sindexDAL,
       'cindexDAL': this.cindexDAL,
+      'dividendDAL': this.dividendDAL,
       'blockchainArchiveDAL': this.blockchainArchiveDAL,
     }
   }
@@ -191,6 +198,7 @@ export class FileDAL {
       this.iindexDAL,
       this.sindexDAL,
       this.cindexDAL,
+      this.dividendDAL,
       this.blockchainArchiveDAL,
     ]
     for (const indexDAL of dals) {
@@ -396,8 +404,36 @@ export class FileDAL {
     return this.cindexDAL.getValidLinksTo(to)
   }
 
-  getAvailableSourcesByPubkey(pubkey:string) {
-    return this.sindexDAL.getAvailableForPubkey(pubkey)
+  async getAvailableSourcesByPubkey(pubkey:string): Promise<HttpSource[]> {
+    const txAvailable = await this.sindexDAL.getAvailableForPubkey(pubkey)
+    const sources: UDSource[] = await this.dividendDAL.getUDSources(pubkey)
+    return sources.map(d => {
+      return {
+        type: 'D',
+        noffset: d.pos,
+        identifier: pubkey,
+        amount: d.amount,
+        base: d.base,
+        conditions: 'SIG(' + pubkey + ')'
+      }
+    }).concat(txAvailable.map(s => {
+      return {
+        type: 'T',
+        noffset: s.pos,
+        identifier: s.identifier,
+        amount: s.amount,
+        base: s.base,
+        conditions: s.conditions
+      }
+    }))
+  }
+
+  async findByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number, isDividend: boolean): Promise<SimpleTxInput[]> {
+    if (isDividend) {
+      return this.dividendDAL.findUdSourceByIdentifierPosAmountBase(identifier, pos, amount, base)
+    } else {
+      return this.sindexDAL.findTxSourceByIdentifierPosAmountBase(identifier, pos, amount, base)
+    }
   }
 
   async getGlobalIdentityByHashForExistence(hash:string): Promise<boolean> {
@@ -810,8 +846,12 @@ export class FileDAL {
     return  this.cindexDAL.existsNonReplayableLink(from, to)
   }
 
-  getSource(identifier:string, pos:number): Promise<FullSindexEntry | null> {
-    return this.sindexDAL.getSource(identifier, pos)
+  async getSource(identifier:string, pos:number, isDividend: boolean): Promise<SimpleTxInput | null> {
+    if (isDividend) {
+      return this.dividendDAL.getUDSource(identifier, pos)
+    } else {
+      return this.sindexDAL.getTxSource(identifier, pos)
+    }
   }
 
   async isMember(pubkey:string):Promise<boolean> {
@@ -973,8 +1013,8 @@ export class FileDAL {
     let iindex = indexer.iindex(index);
     let sindex = indexer.sindex(index);
     let cindex = indexer.cindex(index);
-    sindex = sindex.concat(await indexer.ruleIndexGenDividend(HEAD, iindex, this));
-    sindex = sindex.concat(await indexer.ruleIndexGarbageSmallAccounts(HEAD, sindex, this));
+    const dividends = await indexer.ruleIndexGenDividend(HEAD, iindex, this) // Requires that newcomers are already in DividendDAO
+    sindex = sindex.concat(await indexer.ruleIndexGarbageSmallAccounts(HEAD, sindex, dividends, this));
     cindex = cindex.concat(await indexer.ruleIndexGenCertificationExpiry(HEAD, this));
     mindex = mindex.concat(await indexer.ruleIndexGenMembershipExpiry(HEAD, this));
     iindex = iindex.concat(await indexer.ruleIndexGenExclusionByMembership(HEAD, mindex, this));
@@ -982,7 +1022,7 @@ export class FileDAL {
     mindex = mindex.concat(await indexer.ruleIndexGenImplicitRevocation(HEAD, this));
     await indexer.ruleIndexCorrectMembershipExpiryDate(HEAD, mindex, this);
     await indexer.ruleIndexCorrectCertificationExpiryDate(HEAD, cindex, this);
-    return { HEAD, mindex, iindex, sindex, cindex };
+    return { HEAD, mindex, iindex, sindex, cindex, dividends };
   }
 
   async updateWotbLinks(cindex:CindexEntry[]) {
@@ -990,7 +1030,7 @@ export class FileDAL {
       const from = await this.getWrittenIdtyByPubkeyForWotbID(entry.issuer);
       const to = await this.getWrittenIdtyByPubkeyForWotbID(entry.receiver);
       if (entry.op == CommonConstants.IDX_CREATE) {
-        NewLogger().trace('addLink %s -> %s', from.wotb_id, to.wotb_id)
+        // NewLogger().trace('addLink %s -> %s', from.wotb_id, to.wotb_id)
         this.wotb.addLink(from.wotb_id, to.wotb_id);
       } else {
         // Update = removal
@@ -1108,13 +1148,19 @@ export class FileDAL {
     return history;
   }
 
-  async getUDHistory(pubkey:string) {
-    const sources = await this.sindexDAL.getUDSources(pubkey)
+  async getUDHistory(pubkey:string): Promise<{ history: HttpUD[] }> {
+    const sources: UDSource[] = await this.dividendDAL.getUDSources(pubkey)
     return {
-      history: sources.map((src:SindexEntry) => Underscore.extend({
-        block_number: src.pos,
-        time: src.written_time
-      }, src))
+      history: (await Promise.all<HttpUD>(sources.map(async (src) => {
+        const block = await this.getBlockWeHaveItForSure(src.pos)
+        return {
+          block_number: src.pos,
+          time: block.medianTime,
+          consumed: src.consumed,
+          amount: src.amount,
+          base: src.base
+        }
+      })))
     }
   }
 
@@ -1281,7 +1327,15 @@ export class FileDAL {
   async flushIndexes(indexes: IndexBatch) {
     await this.mindexDAL.insertBatch(indexes.mindex)
     await this.iindexDAL.insertBatch(indexes.iindex)
-    await this.sindexDAL.insertBatch(indexes.sindex)
+    await this.sindexDAL.insertBatch(indexes.sindex.filter(s => s.srcType === 'T')) // We don't store dividends in SINDEX
     await this.cindexDAL.insertBatch(indexes.cindex)
+    await this.dividendDAL.consume(indexes.sindex.filter(s => s.srcType === 'D'))
+  }
+
+  async updateDividend(blockNumber: number, dividend: number|null, unitbase: number, local_iindex: IindexEntry[]): Promise<SimpleUdEntryForWallet[]> {
+    if (dividend) {
+      return this.dividendDAL.produceDividend(blockNumber, dividend, unitbase, local_iindex)
+    }
+    return []
   }
 }
diff --git a/app/lib/dal/indexDAL/abstract/DividendDAO.ts b/app/lib/dal/indexDAL/abstract/DividendDAO.ts
new file mode 100644
index 000000000..bc07529a0
--- /dev/null
+++ b/app/lib/dal/indexDAL/abstract/DividendDAO.ts
@@ -0,0 +1,47 @@
+import {GenericDAO} from "./GenericDAO"
+import {
+  IindexEntry,
+  SimpleSindexEntryForWallet,
+  SimpleTxInput,
+  SimpleUdEntryForWallet,
+  SindexEntry
+} from "../../../indexer"
+
+export interface DividendEntry {
+  pub: string
+  member: boolean
+  availables: number[]
+  consumed: number[]
+  consumedUDs: { dividendNumber: number, dividend: { amount: number, base: number } }[]
+  dividends: { amount: number, base: number }[]
+}
+
+export interface UDSource {
+  consumed: boolean
+  pos: number
+  amount: number
+  base: number
+}
+
+export interface DividendDAO extends GenericDAO<DividendEntry> {
+
+  setMember(member: boolean, pub: string): Promise<void>
+
+  produceDividend(blockNumber: number, dividend: number, unitbase: number, local_iindex: IindexEntry[]): Promise<SimpleUdEntryForWallet[]>
+
+  getUDSources(pub: string): Promise<UDSource[]>
+
+  findUdSourceByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SimpleTxInput[]>
+
+  getUDSource(identifier: string, pos: number): Promise<SimpleTxInput|null>
+
+  createMember(pub: string): Promise<void>
+
+  consume(filter: SindexEntry[]): Promise<void>
+
+  deleteMember(pub: string): Promise<void>
+
+  getWrittenOnUDs(number: number): Promise<SimpleUdEntryForWallet[]>
+
+  revertUDs(number: number): Promise<{ createdUDsDestroyedByRevert: SimpleUdEntryForWallet[], consumedUDsRecoveredByRevert: SimpleUdEntryForWallet[] }>
+}
diff --git a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts
index ac4e52b45..698f580c1 100644
--- a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts
+++ b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts
@@ -26,8 +26,6 @@ export interface IIndexDAO extends ReduceableDAO<IindexEntry> {
 
   getFullFromHash(hash:string): Promise<FullIindexEntry>
 
-  getMembersPubkeys(): Promise<{ pub:string }[]>
-
   getToBeKickedPubkeys(): Promise<string[]>
 
   findAllByWrittenOn(): Promise<IindexEntry[]>
diff --git a/app/lib/dal/indexDAL/abstract/SIndexDAO.ts b/app/lib/dal/indexDAL/abstract/SIndexDAO.ts
index 8f96f4ba5..be5aa1703 100644
--- a/app/lib/dal/indexDAL/abstract/SIndexDAO.ts
+++ b/app/lib/dal/indexDAL/abstract/SIndexDAO.ts
@@ -1,23 +1,24 @@
-import {FullSindexEntry, SindexEntry} from "../../../indexer"
+import {FullSindexEntry, SimpleTxEntryForWallet, SimpleTxInput, SindexEntry} from "../../../indexer"
 import {ReduceableDAO} from "./ReduceableDAO"
 
-export interface SIndexDAO extends ReduceableDAO<SindexEntry> {
+export interface UDSource {
+  consumed: boolean
+  pos: number
+  amount: number
+  base: number
+}
 
-  findByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SindexEntry[]>
+export interface SIndexDAO extends ReduceableDAO<SindexEntry> {
 
-  getSource(identifier:string, pos:number): Promise<FullSindexEntry|null>
+  findTxSourceByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SimpleTxInput[]>
 
-  getUDSources(pubkey:string): Promise<FullSindexEntry[]>
+  getTxSource(identifier:string, pos:number): Promise<FullSindexEntry|null>
 
-  getAvailableForPubkey(pubkey:string): Promise<{ amount:number, base:number }[]>
+  getAvailableForPubkey(pubkey:string): Promise<{ amount:number, base:number, conditions: string, identifier: string, pos: number }[]>
 
   getAvailableForConditions(conditionsStr:string): Promise<SindexEntry[]>
 
   trimConsumedSource(belowNumber:number): Promise<void>
 
-  //---------------------
-  //- TESTING FUNCTIONS -
-  //---------------------
-
-
+  getWrittenOnTxs(blockstamp: string): Promise<SimpleTxEntryForWallet[]>
 }
diff --git a/app/lib/dal/indexDAL/loki/LokiBIndex.ts b/app/lib/dal/indexDAL/loki/LokiBIndex.ts
index 255743c2d..c466e46f7 100644
--- a/app/lib/dal/indexDAL/loki/LokiBIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiBIndex.ts
@@ -1,13 +1,13 @@
-import {LokiIndex} from "./LokiIndex"
 import {DBHead} from "../../../db/DBHead"
 import {BIndexDAO} from "../abstract/BIndexDAO"
 import {NewLogger} from "../../../logger"
 import {getDurationInMicroSeconds, getMicrosecondsTime} from "../../../../ProcessCpuProfiler"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
 
 const logger = NewLogger()
 
-export class LokiBIndex extends LokiIndex<DBHead> implements BIndexDAO {
+export class LokiBIndex extends LokiProtocolIndex<DBHead> implements BIndexDAO {
 
   private HEAD:DBHead|null = null
 
diff --git a/app/lib/dal/indexDAL/loki/LokiBlockchain.ts b/app/lib/dal/indexDAL/loki/LokiBlockchain.ts
index 4a1536355..1c125c194 100644
--- a/app/lib/dal/indexDAL/loki/LokiBlockchain.ts
+++ b/app/lib/dal/indexDAL/loki/LokiBlockchain.ts
@@ -1,9 +1,9 @@
-import {LokiIndex} from "./LokiIndex"
 import {BlockchainDAO} from "../abstract/BlockchainDAO"
 import {DBBlock} from "../../../db/DBBlock"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
 
-export class LokiBlockchain extends LokiIndex<DBBlock> implements BlockchainDAO {
+export class LokiBlockchain extends LokiProtocolIndex<DBBlock> implements BlockchainDAO {
 
   private current:DBBlock|null = null
 
@@ -54,8 +54,12 @@ export class LokiBlockchain extends LokiIndex<DBBlock> implements BlockchainDAO
     return this.insertBatch(blocks)
   }
 
-  async insert(record: DBBlock): Promise<void> {
-    return super.insert(record);
+  async insertBatch(records: DBBlock[]): Promise<void> {
+    const lastInBatch = records[records.length - 1]
+    if (!this.current || this.current.number < lastInBatch.number) {
+      this.current = lastInBatch
+    }
+    return super.insertBatch(records)
   }
 
   async removeBlock(blockstamp: string): Promise<void> {
diff --git a/app/lib/dal/indexDAL/loki/LokiCIndex.ts b/app/lib/dal/indexDAL/loki/LokiCIndex.ts
index 81cc750b2..218ef22ee 100644
--- a/app/lib/dal/indexDAL/loki/LokiCIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiCIndex.ts
@@ -1,10 +1,10 @@
 import {CIndexDAO} from "../abstract/CIndexDAO"
-import {LokiIndex} from "./LokiIndex"
 import {CindexEntry, FullCindexEntry, Indexer} from "../../../indexer"
 import {CommonConstants} from "../../../common-libs/constants"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
 
-export class LokiCIndex extends LokiIndex<CindexEntry> implements CIndexDAO {
+export class LokiCIndex extends LokiProtocolIndex<CindexEntry> implements CIndexDAO {
 
   constructor(loki:any) {
     super(loki, 'cindex', ['issuer', 'receiver'])
diff --git a/app/lib/dal/indexDAL/loki/LokiCollectionManager.ts b/app/lib/dal/indexDAL/loki/LokiCollectionManager.ts
index f922c0fcf..0d6322360 100644
--- a/app/lib/dal/indexDAL/loki/LokiCollectionManager.ts
+++ b/app/lib/dal/indexDAL/loki/LokiCollectionManager.ts
@@ -12,7 +12,7 @@ export abstract class LokiCollectionManager<T> {
 
   public constructor(
     protected loki:any,
-    protected collectionName:'iindex'|'mindex'|'cindex'|'sindex'|'bindex'|'blockchain'|'txs'|'wallet'|'peer',
+    protected collectionName:'iindex'|'mindex'|'cindex'|'sindex'|'bindex'|'blockchain'|'txs'|'wallet'|'peer'|'dividend',
     protected indices: (keyof T)[]) {
     this.collectionIsInitialized = new Promise<void>(res => this.resolveCollection = res)
   }
diff --git a/app/lib/dal/indexDAL/loki/LokiDividend.ts b/app/lib/dal/indexDAL/loki/LokiDividend.ts
new file mode 100644
index 000000000..36d4d42f0
--- /dev/null
+++ b/app/lib/dal/indexDAL/loki/LokiDividend.ts
@@ -0,0 +1,243 @@
+import {LokiIndex} from "./LokiIndex"
+import {DividendDAO, DividendEntry, UDSource} from "../abstract/DividendDAO"
+import {
+  IindexEntry,
+  SimpleSindexEntryForWallet,
+  SimpleTxEntryForWallet,
+  SimpleTxInput,
+  SimpleUdEntryForWallet,
+  SindexEntry
+} from "../../../indexer"
+import {DataErrors} from "../../../common-libs/errors"
+
+export class LokiDividend extends LokiIndex<DividendEntry> implements DividendDAO {
+
+  // private lokiDividend:
+
+  constructor(loki:any) {
+    super(loki, 'dividend', ['pub'])
+  }
+
+  async createMember(pub: string): Promise<void> {
+    const existing = this.collection.find({ pub })[0]
+    if (!existing) {
+      await this.insert({ pub, member: true, availables: [], dividends: [], consumed: [], consumedUDs: [] })
+    } else {
+      await this.setMember(true, pub)
+    }
+  }
+
+  async setMember(member: boolean, pub: string) {
+    await this.collection
+      .chain()
+      .find({ pub })
+      .update(r => {
+        r.member = member
+      })
+  }
+
+  async deleteMember(pub: string): Promise<void> {
+    this.collection
+      .chain()
+      .find({ pub })
+      .remove()
+  }
+
+  async produceDividend(blockNumber: number, dividend: number, unitbase: number, local_iindex: IindexEntry[]): Promise<SimpleUdEntryForWallet[]> {
+    const dividends: SimpleUdEntryForWallet[] = []
+    // Then produce the UD
+    this.collection
+      .chain()
+      .find({ member: true })
+      .update(r => {
+        r.availables.push(blockNumber)
+        r.dividends.push({ amount: dividend, base: unitbase })
+        dividends.push({
+          srcType: 'D',
+          amount: dividend,
+          base: unitbase,
+          conditions: 'SIG(' + r.pub + ')',
+          op: 'CREATE',
+          identifier: r.pub,
+          pos: blockNumber
+        })
+      })
+    return dividends
+  }
+
+  async consume(filter: SindexEntry[]): Promise<void> {
+    for (const dividendToConsume of filter) {
+      this.collection
+        .chain()
+        .find({
+          pub: dividendToConsume.identifier
+        })
+        .update(m => {
+          const index = m.availables.indexOf(dividendToConsume.pos)
+
+          // We add it to the consumption history
+          m.consumed.push(dividendToConsume.writtenOn)
+          m.consumedUDs.push({
+            dividendNumber: dividendToConsume.pos,
+            dividend: m.dividends[index]
+          })
+
+          // We remove it from available dividends
+          m.availables.splice(index, 1)
+          m.dividends.splice(index, 1)
+        })
+    }
+  }
+
+  async getUDSources(pub: string): Promise<UDSource[]> {
+    const member = this.collection
+      .chain()
+      .find({ pub })
+      .data()[0]
+    if (!member) {
+      return []
+    }
+    return member.availables.map(pos => this.toUDSource(member, pos) as UDSource)
+  }
+
+  async findUdSourceByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SimpleTxInput[]> {
+    const member = this.collection.find({ pub: identifier })[0]
+    let src: UDSource|null = null
+    if (member) {
+      const udSrc = this.toUDSource(member, pos)
+      if (udSrc && udSrc.amount === amount && udSrc.base === base) {
+        src = udSrc
+      }
+    }
+    return [{
+      written_time: 0,
+      conditions: 'SIG(' + identifier + ')',
+      consumed: !src,
+      amount,
+      base
+    }]
+  }
+
+  private toUDSource(entry: DividendEntry, pos: number): UDSource|null {
+    const index = entry.availables.indexOf(pos)
+    if (index === -1) {
+      return null
+    }
+    const src = entry.dividends[index]
+    return {
+      consumed: false,
+      pos,
+      amount: src.amount,
+      base: src.base,
+    }
+  }
+
+  async getUDSource(identifier: string, pos: number): Promise<SimpleTxInput|null> {
+    const member = this.collection.find({ pub: identifier })[0]
+    let src: UDSource|null = null
+    if (member) {
+      src = this.toUDSource(member, pos)
+    }
+    if (!src) {
+      return null
+    }
+    return {
+      written_time: 0,
+      conditions: 'SIG(' + identifier + ')',
+      consumed: !src,
+      amount: src.amount,
+      base: src.base
+    }
+  }
+
+  async getWrittenOn(blockstamp: string): Promise<DividendEntry[]> {
+    throw Error(DataErrors[DataErrors.LOKI_DIVIDEND_GET_WRITTEN_ON_SHOULD_NOT_BE_USED])
+  }
+
+  async getWrittenOnUDs(number: number): Promise<SimpleUdEntryForWallet[]> {
+    const res: SimpleUdEntryForWallet[] = []
+    this.collection
+      .chain()
+      .find({ availables: { $contains: number } })
+      .data()
+      .map(m => {
+        const s = this.toUDSource(m, number) as UDSource
+        res.push({
+          srcType: 'D',
+          op: 'CREATE',
+          conditions: 'SIG(' + m.pub + ')',
+          amount: s.amount,
+          base: s.base,
+          identifier: m.pub,
+          pos: s.pos
+        })
+      })
+    return res
+  }
+
+  async removeBlock(blockstamp: string): Promise<void> {
+    throw Error(DataErrors[DataErrors.LOKI_DIVIDEND_REMOVE_BLOCK_SHOULD_NOT_BE_USED])
+  }
+
+  /**
+   * Remove UD data produced in a block, either UD production or UD consumption.
+   * @param {number} number Block number to revert the created UDs.
+   * @returns {Promise<{createdUDsDestroyedByRevert: SimpleUdEntryForWallet[]}>}
+   */
+  async revertUDs(number: number): Promise<{
+    createdUDsDestroyedByRevert: SimpleUdEntryForWallet[]
+    consumedUDsRecoveredByRevert: SimpleUdEntryForWallet[]
+  }> {
+    const createdUDsDestroyedByRevert: SimpleUdEntryForWallet[] = []
+    const consumedUDsRecoveredByRevert: SimpleUdEntryForWallet[] = []
+    // Remove produced dividends at this block
+    this.collection
+      .chain()
+      .find({ availables: { $contains: number }})
+      .update(m => {
+        const index = m.availables.indexOf(number)
+        const src = m.dividends[index]
+        createdUDsDestroyedByRevert.push({
+          conditions: 'SIG(' + m.pub + ')',
+          pos: number,
+          identifier: m.pub,
+          amount: src.amount,
+          base: src.base,
+          srcType: 'D',
+          op: 'CREATE'
+        })
+        m.availables.splice(index, 1)
+        m.dividends.splice(index, 1)
+      })
+    // Unconsumed dividends consumed at this block
+    this.collection
+      .chain()
+      .find({ consumed: { $contains: number }})
+      .update(m => {
+        const index = m.consumed.indexOf(number)
+
+        const src = m.consumedUDs[index].dividend
+        consumedUDsRecoveredByRevert.push({
+          conditions: 'SIG(' + m.pub + ')',
+          pos: m.consumedUDs[index].dividendNumber,
+          identifier: m.pub,
+          amount: src.amount,
+          base: src.base,
+          srcType: 'D',
+          op: 'CREATE'
+        })
+
+        // We put it back as available
+        m.availables.push(m.consumedUDs[index].dividendNumber)
+        m.dividends.push(m.consumedUDs[index].dividend)
+
+        // We remove it from consumed
+        m.consumed.splice(index, 1)
+        m.consumedUDs.splice(index, 1)
+      })
+    return {
+      createdUDsDestroyedByRevert,
+      consumedUDsRecoveredByRevert,
+    }
+  }
+}
diff --git a/app/lib/dal/indexDAL/loki/LokiIIndex.ts b/app/lib/dal/indexDAL/loki/LokiIIndex.ts
index d0fc9cfdc..e0effa5d8 100644
--- a/app/lib/dal/indexDAL/loki/LokiIIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiIIndex.ts
@@ -2,7 +2,6 @@ import {FullIindexEntry, IindexEntry, Indexer} from "../../../indexer"
 import {IIndexDAO} from "../abstract/IIndexDAO"
 import {LokiPubkeySharingIndex} from "./LokiPubkeySharingIndex"
 import {OldIindexEntry} from "../../../db/OldIindexEntry"
-import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
 
 export class LokiIIndex extends LokiPubkeySharingIndex<IindexEntry> implements IIndexDAO {
 
@@ -129,11 +128,6 @@ export class LokiIIndex extends LokiPubkeySharingIndex<IindexEntry> implements I
     ) as Promise<FullIindexEntry|null>
   }
 
-  @MonitorLokiExecutionTime()
-  async getMembersPubkeys(): Promise<{ pub: string }[]> {
-    return (await this.getMembers()).map(m => ({ pub: m.pubkey }))
-  }
-
   async getToBeKickedPubkeys(): Promise<string[]> {
     return this.collection
     // Those who are still marked member somewhere
diff --git a/app/lib/dal/indexDAL/loki/LokiIndex.ts b/app/lib/dal/indexDAL/loki/LokiIndex.ts
index e4551fb26..877cdc511 100644
--- a/app/lib/dal/indexDAL/loki/LokiIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiIndex.ts
@@ -2,12 +2,7 @@ import {GenericDAO} from "../abstract/GenericDAO"
 import {LokiCollectionManager} from "./LokiCollectionManager"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
 
-export interface IndexData {
-  written_on: string
-  writtenOn: number
-}
-
-export abstract class LokiIndex<T extends IndexData> extends LokiCollectionManager<T> implements GenericDAO<T> {
+export abstract class LokiIndex<T> extends LokiCollectionManager<T> implements GenericDAO<T> {
 
   cleanCache(): void {
   }
@@ -35,15 +30,6 @@ export abstract class LokiIndex<T extends IndexData> extends LokiCollectionManag
     records.map(r => this.insert(r))
   }
 
-  @MonitorLokiExecutionTime(true)
-  async getWrittenOn(blockstamp: string): Promise<T[]> {
-    const criterion:any = { writtenOn: parseInt(blockstamp) }
-    return this.collection.find(criterion)
-  }
-
-  @MonitorLokiExecutionTime(true)
-  async removeBlock(blockstamp: string): Promise<void> {
-    const data = await this.getWrittenOn(blockstamp)
-    data.map(d => this.collection.remove(d))
-  }
+  abstract getWrittenOn(blockstamp: string): Promise<T[]>
+  abstract removeBlock(blockstamp: string): Promise<void>
 }
diff --git a/app/lib/dal/indexDAL/loki/LokiProtocolIndex.ts b/app/lib/dal/indexDAL/loki/LokiProtocolIndex.ts
new file mode 100644
index 000000000..90848ed99
--- /dev/null
+++ b/app/lib/dal/indexDAL/loki/LokiProtocolIndex.ts
@@ -0,0 +1,23 @@
+import {GenericDAO} from "../abstract/GenericDAO"
+import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiIndex} from "./LokiIndex"
+
+export interface IndexData {
+  written_on: string
+  writtenOn: number
+}
+
+export abstract class LokiProtocolIndex<T extends IndexData> extends LokiIndex<T> implements GenericDAO<T> {
+
+  @MonitorLokiExecutionTime(true)
+  async getWrittenOn(blockstamp: string): Promise<T[]> {
+    const criterion:any = { writtenOn: parseInt(blockstamp) }
+    return this.collection.find(criterion)
+  }
+
+  @MonitorLokiExecutionTime(true)
+  async removeBlock(blockstamp: string): Promise<void> {
+    const data = await this.getWrittenOn(blockstamp)
+    data.map(d => this.collection.remove(d))
+  }
+}
diff --git a/app/lib/dal/indexDAL/loki/LokiPubkeySharingIndex.ts b/app/lib/dal/indexDAL/loki/LokiPubkeySharingIndex.ts
index e44ee62f2..8b0d43274 100644
--- a/app/lib/dal/indexDAL/loki/LokiPubkeySharingIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiPubkeySharingIndex.ts
@@ -1,8 +1,9 @@
 import {Indexer} from "../../../indexer"
 import {LokiIndex} from "./LokiIndex"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
 
-export class LokiPubkeySharingIndex<T extends { written_on:string, writtenOn:number, pub:string }> extends LokiIndex<T> {
+export class LokiPubkeySharingIndex<T extends { written_on:string, writtenOn:number, pub:string }> extends LokiProtocolIndex<T> {
 
   @MonitorLokiExecutionTime(true)
   async trimRecords(belowNumber: number): Promise<void> {
diff --git a/app/lib/dal/indexDAL/loki/LokiSIndex.ts b/app/lib/dal/indexDAL/loki/LokiSIndex.ts
index 2dbbfb249..180437f29 100644
--- a/app/lib/dal/indexDAL/loki/LokiSIndex.ts
+++ b/app/lib/dal/indexDAL/loki/LokiSIndex.ts
@@ -1,16 +1,27 @@
 import {LokiIndex} from "./LokiIndex"
-import {FullSindexEntry, Indexer, SindexEntry} from "../../../indexer"
+import {
+  FullSindexEntry,
+  Indexer,
+  SimpleSindexEntryForWallet,
+  SimpleTxEntryForWallet,
+  SindexEntry
+} from "../../../indexer"
 import {SIndexDAO} from "../abstract/SIndexDAO"
 import {Underscore} from "../../../common-libs/underscore"
 import {MonitorLokiExecutionTime} from "../../../debug/MonitorLokiExecutionTime"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
+import {LokiDividend} from "./LokiDividend"
 
-export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO {
+export class LokiSIndex extends LokiProtocolIndex<SindexEntry> implements SIndexDAO {
+
+  private lokiDividend: LokiDividend
 
   constructor(loki:any) {
     super(loki, 'sindex', ['identifier', 'conditions', 'writtenOn'])
+    this.lokiDividend = new LokiDividend(loki)
   }
 
-  async findByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SindexEntry[]> {
+  async findTxSourceByIdentifierPosAmountBase(identifier: string, pos: number, amount: number, base: number): Promise<SindexEntry[]> {
     return this.collection
       .chain()
       .find({ identifier, pos, amount, base })
@@ -36,7 +47,7 @@ export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO {
     return Underscore.sortBy(sources, (row:SindexEntry) => row.type == 'D' ? 0 : 1)
   }
 
-  async getAvailableForPubkey(pubkey: string): Promise<{ amount: number; base: number }[]> {
+  async getAvailableForPubkey(pubkey: string): Promise<{ amount: number; base: number, conditions: string, identifier: string, pos: number }[]> {
     return this.collection
       .chain()
       .find({ conditions: { $regex: 'SIG\\(' + pubkey + '\\)' } })
@@ -49,11 +60,11 @@ export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO {
       })
   }
 
-  async getSource(identifier: string, pos: number): Promise<FullSindexEntry | null> {
+  async getTxSource(identifier: string, pos: number): Promise<FullSindexEntry | null> {
     const reducables = this.collection
       .chain()
       .find({ identifier, pos })
-      .simplesort('writtenOn')
+      .compoundsort([['writtenOn', false], ['op', false]])
       .data()
       .map(src => {
         src.type = src.tx ? 'T' : 'D'
@@ -65,24 +76,6 @@ export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO {
     return Indexer.DUP_HELPERS.reduce(reducables)
   }
 
-  async getUDSources(pubkey: string): Promise<FullSindexEntry[]> {
-    const reducables = this.collection
-      .chain()
-      .find({
-        $and: [
-          { tx: null },
-          { conditions: 'SIG(' + pubkey + ')' },
-        ]
-      })
-      .simplesort('writtenOn')
-      .data()
-      .map(src => {
-        src.type = src.tx ? 'T' : 'D'
-        return src
-      })
-    return Indexer.DUP_HELPERS.reduceBy(reducables, ['identifier', 'pos'])
-  }
-
   @MonitorLokiExecutionTime(true)
   async trimConsumedSource(belowNumber: number): Promise<void> {
     const consumed = this.collection
@@ -108,5 +101,20 @@ export class LokiSIndex extends LokiIndex<SindexEntry> implements SIndexDAO {
     return this.trimConsumedSource(belowNumber)
   }
 
-
+  async getWrittenOnTxs(blockstamp: string): Promise<SimpleTxEntryForWallet[]> {
+    const entries = (await this.getWrittenOn(blockstamp))
+    const res: SimpleTxEntryForWallet[] = []
+    entries.forEach(s => {
+      res.push({
+        srcType: 'T',
+        op: s.op,
+        conditions: s.conditions,
+        amount: s.amount,
+        base: s.base,
+        identifier: s.identifier,
+        pos: s.pos
+      })
+    })
+    return res
+  }
 }
diff --git a/app/lib/dal/indexDAL/loki/LokiTransactions.ts b/app/lib/dal/indexDAL/loki/LokiTransactions.ts
index 76f87a2a0..7db59faf4 100644
--- a/app/lib/dal/indexDAL/loki/LokiTransactions.ts
+++ b/app/lib/dal/indexDAL/loki/LokiTransactions.ts
@@ -18,10 +18,11 @@ import {SandBox} from "../../sqliteDAL/SandBox"
 import {TransactionDTO} from "../../../dto/TransactionDTO"
 import {DBTx} from "../../../db/DBTx"
 import {Underscore} from "../../../common-libs/underscore"
+import {LokiProtocolIndex} from "./LokiProtocolIndex"
 
 const constants = require('../../../constants')
 
-export class LokiTransactions extends LokiIndex<DBTx> implements TxsDAO {
+export class LokiTransactions extends LokiProtocolIndex<DBTx> implements TxsDAO {
 
   constructor(loki: any) {
     super(loki, 'txs', [])
diff --git a/app/lib/dto/BlockDTO.ts b/app/lib/dto/BlockDTO.ts
index dba24f303..7ee505e4e 100644
--- a/app/lib/dto/BlockDTO.ts
+++ b/app/lib/dto/BlockDTO.ts
@@ -32,7 +32,7 @@ export class BlockDTO implements Cloneable {
   previousHash: string
   issuer: string
   previousIssuer: string
-  dividend: number
+  dividend: number|null
   time: number
   powMin: number
   unitbase: number
diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts
index b443b1a80..3aefab836 100644
--- a/app/lib/dto/TransactionDTO.ts
+++ b/app/lib/dto/TransactionDTO.ts
@@ -23,7 +23,7 @@ export class InputDTO implements BaseDTO {
   constructor(
     public amount: number,
     public base: number,
-    public type: string,
+    public type: 'T'|'D',
     public identifier: string,
     public pos: number,
     public raw: string
@@ -134,7 +134,7 @@ export class TransactionDTO implements Cloneable {
       return new InputDTO(
         parseInt(amount),
         parseInt(base),
-        type,
+        type as 'T'|'D',
         identifier,
         parseInt(pos),
         input
diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts
index 6e5616d20..4220688dc 100644
--- a/app/lib/indexer.ts
+++ b/app/lib/indexer.ts
@@ -28,6 +28,7 @@ import {DBBlock} from "./db/DBBlock"
 import {DBWallet} from "./db/DBWallet"
 import {Tristamp} from "./common/Tristamp"
 import {Underscore} from "./common-libs/underscore"
+import {DataErrors} from "./common-libs/errors"
 
 const constants       = CommonConstants
 
@@ -146,6 +147,7 @@ export interface FullCindexEntry {
 }
 
 export interface SindexEntry extends IndexEntry {
+  srcType: 'T'|'D'
   tx: string | null,
   identifier: string,
   pos: number,
@@ -179,6 +181,37 @@ export interface FullSindexEntry {
   consumed: boolean
 }
 
+export interface SimpleTxInput {
+  conditions: string
+  consumed: boolean
+  written_time: number
+  amount: number
+  base: number
+}
+
+export interface BasedAmount {
+  amount: number
+  base: number
+}
+
+export interface SimpleSindexEntryForWallet {
+  op: string
+  srcType: 'T'|'D'
+  conditions: string
+  amount: number
+  base: number
+  identifier: string
+  pos: number
+}
+
+export interface SimpleTxEntryForWallet extends SimpleSindexEntryForWallet {
+  srcType: 'T'
+}
+
+export interface SimpleUdEntryForWallet extends SimpleSindexEntryForWallet {
+  srcType: 'D'
+}
+
 export interface Ranger {
   (n:number, m:number): Promise<DBHead[]>
 }
@@ -461,6 +494,7 @@ export class Indexer {
         index.push({
           index: constants.S_INDEX,
           op: constants.IDX_UPDATE,
+          srcType: input.type,
           tx: txHash,
           identifier: input.identifier,
           pos: input.pos,
@@ -485,6 +519,7 @@ export class Indexer {
         index.push({
           index: constants.S_INDEX,
           op: constants.IDX_CREATE,
+          srcType: 'T',
           tx: txHash,
           identifier: txHash,
           pos: i++,
@@ -537,7 +572,7 @@ export class Indexer {
     HEAD.powMin = block.powMin
     HEAD.unitBase = block.unitbase
     HEAD.membersCount = block.membersCount
-    HEAD.dividend = block.dividend
+    HEAD.dividend = block.dividend || 0
     HEAD.new_dividend = null
 
     const HEAD_1 = await head(1);
@@ -972,18 +1007,19 @@ export class Indexer {
       }
     }))
 
-    const getInputLocalFirstOrFallbackGlobally = async (sindex:SindexEntry[], ENTRY:SindexEntry) => {
-      let source = Underscore.filter(sindex, src =>
+    const getInputLocalFirstOrFallbackGlobally = async (sindex:SindexEntry[], ENTRY:SindexEntry): Promise<SimpleTxInput> => {
+      let source: SimpleTxInput|null = Underscore.filter(sindex, src =>
         src.identifier == ENTRY.identifier
         && src.pos == ENTRY.pos
         && src.conditions !== ''
         && src.op === constants.IDX_CREATE)[0];
       if (!source) {
-        const reducable = await dal.sindexDAL.findByIdentifierPosAmountBase(
+        const reducable = await dal.findByIdentifierPosAmountBase(
           ENTRY.identifier,
           ENTRY.pos,
           ENTRY.amount,
-          ENTRY.base
+          ENTRY.base,
+          ENTRY.srcType === 'D'
         );
         source = reduce(reducable)
       }
@@ -994,7 +1030,7 @@ export class Indexer {
     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;
+      ENTRY.available = !source.consumed
     }))
 
     // BR_G47
@@ -1640,34 +1676,22 @@ export class Indexer {
   }
 
   // BR_G91
-  static async ruleIndexGenDividend(HEAD: DBHead, local_iindex: IindexEntry[], dal: FileDAL) {
-    const dividends = [];
+  static async ruleIndexGenDividend(HEAD: DBHead, local_iindex: IindexEntry[], dal: FileDAL): Promise<SimpleUdEntryForWallet[]> {
+    // Create the newcomers first, as they will produce a dividend too
+    for (const newcomer of local_iindex) {
+      await dal.dividendDAL.createMember(newcomer.pub)
+    }
     if (HEAD.new_dividend) {
-      const members = (await dal.iindexDAL.getMembersPubkeys()).concat(local_iindex.filter(i => i.member))
-      for (const MEMBER of members) {
-        dividends.push({
-          index: constants.S_INDEX,
-          op: 'CREATE',
-          tx: null,
-          identifier: MEMBER.pub,
-          pos: HEAD.number,
-          written_on: [HEAD.number, HEAD.hash].join('-'),
-          writtenOn: HEAD.number,
-          written_time: HEAD.medianTime,
-          amount: HEAD.dividend,
-          base: HEAD.unitBase,
-          locktime: null,
-          conditions: 'SIG(' + MEMBER.pub + ')',
-          consumed: false
-        });
-      }
+      return dal.updateDividend(HEAD.number, HEAD.new_dividend, HEAD.unitBase, local_iindex)
     }
-    return dividends;
+    return []
   }
 
   // BR_G106
-  static async ruleIndexGarbageSmallAccounts(HEAD: DBHead, sindex: SindexEntry[], dal:AccountsGarbagingDAL) {
-    const garbages = [];
+  static async ruleIndexGarbageSmallAccounts(HEAD: DBHead, transactions: SindexEntry[], dividends: SimpleUdEntryForWallet[], dal:AccountsGarbagingDAL) {
+    let sindex: SimpleSindexEntryForWallet[] = transactions
+    sindex = sindex.concat(dividends)
+    const garbages: SindexEntry[] = [];
     const accounts = Object.keys(sindex.reduce((acc: { [k:string]: boolean }, src) => {
       acc[src.conditions] = true;
       return acc;
@@ -1677,7 +1701,7 @@ export class Indexer {
       return map;
     }, {});
     for (const account of accounts) {
-      const localAccountEntries = Underscore.filter(sindex, (src:SindexEntry) => src.conditions == account);
+      const localAccountEntries = Underscore.filter(sindex, src => src.conditions == account)
       const wallet = await wallets[account];
       const balance = wallet.balance
       const variations = localAccountEntries.reduce((sum:number, src:SindexEntry) => {
@@ -1687,15 +1711,19 @@ export class Indexer {
           return sum - src.amount * Math.pow(10, src.base);
         }
       }, 0)
-      // console.log('Balance of %s = %s (%s)', account, balance, variations > 0 ? '+' + variations : variations)
-      if (balance + variations < constants.ACCOUNT_MINIMUM_CURRENT_BASED_AMOUNT * Math.pow(10, HEAD.unitBase)) {
+      if (balance + variations < 0) {
+        throw Error(DataErrors[DataErrors.NEGATIVE_BALANCE])
+      }
+      else 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 = Underscore.filter(sindex, (entry:SindexEntry) => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0;
+          const sourceBeingConsumed = Underscore.filter(sindex, entry => entry.op === 'UPDATE' && entry.identifier == src.identifier && entry.pos == src.pos).length > 0;
           if (!sourceBeingConsumed) {
             garbages.push({
+              index: 'SINDEX',
               op: 'UPDATE',
-              tx: src.tx,
+              srcType: 'T',
+              tx: null,
               identifier: src.identifier,
               pos: src.pos,
               amount: src.amount,
@@ -1704,7 +1732,14 @@ export class Indexer {
               writtenOn: HEAD.number,
               written_time: HEAD.medianTime,
               conditions: src.conditions,
-              consumed: true // It is now consumed
+              consumed: true, // It is now consumed
+
+              // TODO: make these fields not required using good types
+              created_on: '',
+              locktime: 0,
+              unlock: null,
+              txObj: {} as TransactionDTO,
+              age: 0,
             });
           }
         }
@@ -1929,7 +1964,7 @@ function blockstamp(aNumber: number, aHash: string) {
 
 function reduce<T>(records: T[]): T {
   return records.reduce((obj:T, record) => {
-    const keys = Underscore.keys(record)
+    const keys = Object.keys(record) as (keyof T)[]
     for (const k of keys) {
       if (record[k] !== undefined && record[k] !== null) {
         obj[k] = record[k];
@@ -2087,7 +2122,7 @@ async function checkCertificationIsValid (block: BlockDTO, cert: CindexEntry, fi
   }
 }
 
-function txSourceUnlock(ENTRY:SindexEntry, source:SindexEntry, HEAD: DBHead) {
+function txSourceUnlock(ENTRY:SindexEntry, source:{ conditions: string, written_time: number}, HEAD: DBHead) {
   const tx = ENTRY.txObj;
   const unlockParams:string[] = TransactionDTO.unlock2params(ENTRY.unlock || '')
   const unlocksMetadata:UnlockMetadata = {}
diff --git a/app/lib/other_constants.ts b/app/lib/other_constants.ts
index 2e46465ee..2b0139b50 100644
--- a/app/lib/other_constants.ts
+++ b/app/lib/other_constants.ts
@@ -24,4 +24,5 @@ export const OtherConstants = {
 
   ENABLE_LOKI_MONITORING: false,
   ENABLE_SQL_MONITORING: false,
+  TRACE_BALANCES: false
 }
\ No newline at end of file
diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts
index 417b91c35..57721cfb1 100644
--- a/app/lib/rules/global_rules.ts
+++ b/app/lib/rules/global_rules.ts
@@ -21,7 +21,7 @@ import {rawer, txunlock} from "../common-libs/index"
 import {CommonConstants} from "../common-libs/constants"
 import {IdentityDTO} from "../dto/IdentityDTO"
 import {hashf} from "../common"
-import {Indexer} from "../indexer"
+import {Indexer, SimpleTxInput} from "../indexer"
 import {DBTx} from "../db/DBTx"
 import {Tristamp} from "../common/Tristamp"
 
@@ -109,7 +109,7 @@ export const GLOBAL_RULES_FUNCTIONS = {
       }
       for (let k = 0, len2 = inputs.length; k < len2; k++) {
         let src = inputs[k];
-        let dbSrc = await dal.getSource(src.identifier, src.pos);
+        let dbSrc: SimpleTxInput|null = await dal.getSource(src.identifier, src.pos, src.type === 'D');
         logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, src.identifier, src.pos, dbSrc && dbSrc.consumed);
         if (!dbSrc) {
           // For chained transactions which are checked on sandbox submission, we accept them if there is already
diff --git a/app/modules/bma/lib/controllers/transactions.ts b/app/modules/bma/lib/controllers/transactions.ts
index e921f4d88..a56834b50 100644
--- a/app/modules/bma/lib/controllers/transactions.ts
+++ b/app/modules/bma/lib/controllers/transactions.ts
@@ -16,7 +16,7 @@ import {ParametersService} from "../parameters";
 import {Source} from "../entity/source";
 import {BMAConstants} from "../constants";
 import {TransactionDTO} from "../../../../lib/dto/TransactionDTO";
-import {HttpSources, HttpTransaction, HttpTxHistory, HttpTxOfHistory, HttpTxPending} from "../dtos";
+import {HttpSource, HttpSources, HttpTransaction, HttpTxHistory, HttpTxOfHistory, HttpTxPending} from "../dtos";
 import {DBTx} from "../../../../lib/db/DBTx"
 import {Underscore} from "../../../../lib/common-libs/underscore"
 
@@ -45,15 +45,11 @@ export class TransactionBinding extends AbstractController {
   async getSources(req:any): Promise<HttpSources> {
     const pubkey = await ParametersService.getPubkeyP(req);
     const sources = await this.server.dal.getAvailableSourcesByPubkey(pubkey);
-    const result:any = {
-      "currency": this.conf.currency,
-      "pubkey": pubkey,
-      "sources": []
-    };
-    sources.forEach(function (src:any) {
-      result.sources.push(new Source(src).json());
-    });
-    return result;
+    return {
+      currency: this.conf.currency,
+      pubkey,
+      sources
+    }
   }
 
   async getByHash(req:any): Promise<HttpTransaction> {
diff --git a/test/dal/sources-dal.ts b/test/dal/sources-dal.ts
index 58a45b06a..3288f5031 100644
--- a/test/dal/sources-dal.ts
+++ b/test/dal/sources-dal.ts
@@ -39,11 +39,9 @@ describe("Source DAL", function(){
     sourcesOfDEF.should.have.length(1);
     const sourcesOfABC = await dal.sindexDAL.getAvailableForPubkey('ABC');
     sourcesOfABC.should.have.length(1);
-    const source1 = await dal.sindexDAL.getSource('SOURCE_1', 4) as any
+    const source1 = await dal.sindexDAL.getTxSource('SOURCE_1', 4) as any
     source1.should.have.property('consumed').equal(true);
-    const udSources = await dal.sindexDAL.getUDSources('ABC');
-    udSources.should.have.length(2);
-    udSources[0].should.have.property('consumed').equal(false);
-    udSources[1].should.have.property('consumed').equal(true);
+    const source2 = await dal.sindexDAL.getTxSource('SOURCE_2', 4) as any
+    source2.should.have.property('consumed').equal(false);
   })
 })
diff --git a/test/fast/dal/basic-loki.ts b/test/fast/dal/basic-loki.ts
index d95f0b1a3..2f26a3b84 100644
--- a/test/fast/dal/basic-loki.ts
+++ b/test/fast/dal/basic-loki.ts
@@ -13,6 +13,7 @@
 
 import * as assert from "assert"
 import {LokiIndex} from "../../../app/lib/dal/indexDAL/loki/LokiIndex"
+import {LokiProtocolIndex} from "../../../app/lib/dal/indexDAL/loki/LokiProtocolIndex"
 
 const loki = require('lokijs')
 
@@ -24,7 +25,7 @@ interface TestEntity {
 
 let lokiIndex:LokiIndex<TestEntity>
 
-class TheIndex extends LokiIndex<TestEntity> {
+class TheIndex extends LokiProtocolIndex<TestEntity> {
 }
 
 describe("Basic LokiJS database", () => {
diff --git a/test/fast/protocol/protocol-brg106-number.ts b/test/fast/protocol/protocol-brg106-number.ts
index 0af3d3f9c..cf3675c14 100644
--- a/test/fast/protocol/protocol-brg106-number.ts
+++ b/test/fast/protocol/protocol-brg106-number.ts
@@ -62,33 +62,33 @@ describe("Protocol BR_G106 - Garbaging", function(){
       // C sends 100 to E --> C keeps 0
       { op: 'UPDATE', conditions: 'pubkeyC', amount: 100, base: 0, identifier: 'I8', pos: 0 },
       { op: 'CREATE', conditions: 'pubkeyE', amount: 100, base: 0, identifier: 'I9', pos: 0 }
-      ] as any, dal as any);
+      ] as any, [], dal as any);
     cleaning.should.have.length(5);
     cleaning[0].should.have.property('identifier').equal('I3');
     cleaning[0].should.have.property('amount').equal(3);
     cleaning[0].should.have.property('base').equal(0);
-    cleaning[0].should.have.property('tx').equal(undefined);
+    cleaning[0].should.have.property('tx').equal(null);
     cleaning[0].should.have.property('consumed').equal(true);
     cleaning[0].should.have.property('op').equal('UPDATE');
 
     cleaning[1].should.have.property('identifier').equal('I7');
     cleaning[1].should.have.property('amount').equal(5);
     cleaning[1].should.have.property('base').equal(0);
-    cleaning[1].should.have.property('tx').equal(undefined);
+    cleaning[1].should.have.property('tx').equal(null);
     cleaning[1].should.have.property('consumed').equal(true);
     cleaning[1].should.have.property('op').equal('UPDATE');
 
     cleaning[2].should.have.property('identifier').equal('I5');
     cleaning[2].should.have.property('amount').equal(9);
     cleaning[2].should.have.property('base').equal(0);
-    cleaning[2].should.have.property('tx').equal(undefined);
+    cleaning[2].should.have.property('tx').equal(null);
     cleaning[2].should.have.property('consumed').equal(true);
     cleaning[2].should.have.property('op').equal('UPDATE');
 
     cleaning[3].should.have.property('identifier').equal('I10');
     cleaning[3].should.have.property('amount').equal(22);
     cleaning[3].should.have.property('base').equal(0);
-    cleaning[3].should.have.property('tx').equal('B2');
+    cleaning[3].should.have.property('tx').equal(null);
     cleaning[3].should.have.property('consumed').equal(true);
     cleaning[3].should.have.property('op').equal('UPDATE');
 
diff --git a/test/integration/branches/branches_revert2.ts b/test/integration/branches/branches_revert2.ts
index 34d59d316..f00cf81a5 100644
--- a/test/integration/branches/branches_revert2.ts
+++ b/test/integration/branches/branches_revert2.ts
@@ -102,7 +102,7 @@ describe("Revert two blocks", function() {
       });
     });
 
-    it('/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd should have only UD', function() {
+    it('/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd should have nothing left because of garbaging', function() {
       return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'), (body:string) => {
         let res = JSON.parse(body);
         res.sources.should.have.length(0)
@@ -132,7 +132,7 @@ describe("Revert two blocks", function() {
     });
   })
 
-  describe("after revert", () => {
+  describe("after revert of transaction", () => {
 
     before(async () => {
       await s1.revert();
@@ -185,10 +185,49 @@ describe("Revert two blocks", function() {
     });
   })
 
+  describe("after revert of UD", () => {
+
+    before(async () => {
+      await s1.revert();
+    })
+
+    it('/block/0 should exist', function() {
+      return expectJSON(rp('http://127.0.0.1:7712/blockchain/block/0', { json: true }), {
+        number: 0
+      });
+    });
+
+    it('/block/2 should NOT exist', function() {
+      return expectHttpCode(404, rp('http://127.0.0.1:7712/blockchain/block/2', { json: true }));
+    });
+
+    it('/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd should have nothing', function() {
+      return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'), (body:string) => {
+        let res = JSON.parse(body);
+        res.sources.should.have.length(0)
+      });
+    });
+
+    it('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo should have nothing', function() {
+      return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'), (body:string) => {
+        let res = JSON.parse(body);
+        res.sources.should.have.length(0)
+      });
+    });
+
+    it('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV should have nothing', function() {
+      return expectAnswer(rp('http://127.0.0.1:7712/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'), (body:string) => {
+        let res = JSON.parse(body);
+        res.sources.should.have.length(0)
+      });
+    });
+  })
+
   describe("commit again (but send less, to check that the account is not cleaned this time)", () => {
 
     before(async () => {
       await s1.dal.txsDAL.removeAll()
+      await s1.resolveExistingBlock(1) // UD block
       await cat.sendMoney(19, toc);
       await s1.dal.blockDAL.removeBlock('DELETE FROM block WHERE fork AND number = 3')
       await s1.commit({ time: now + 1 });
-- 
GitLab