diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index 7b0586869ed593c12a70907170d31f6e3deb72a5..5e3246d773cd552744358e8a7ee3e67e37ed2bb7 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -1047,6 +1047,7 @@ export class FileDAL { await this.mindexDAL.trimRecords(maxNumber) await this.cindexDAL.trimExpiredCerts(maxNumber) await this.sindexDAL.trimConsumedSource(maxNumber) + await this.dividendDAL.trimConsumedUDs(maxNumber) } } diff --git a/app/lib/dal/indexDAL/abstract/DividendDAO.ts b/app/lib/dal/indexDAL/abstract/DividendDAO.ts index bc07529a0b7a43fa5a2d88cca2d67e89003a1b9c..69e74c7a7b02bc8483aededd6b54c6feea6631e8 100644 --- a/app/lib/dal/indexDAL/abstract/DividendDAO.ts +++ b/app/lib/dal/indexDAL/abstract/DividendDAO.ts @@ -1,18 +1,21 @@ import {GenericDAO} from "./GenericDAO" -import { - IindexEntry, - SimpleSindexEntryForWallet, - SimpleTxInput, - SimpleUdEntryForWallet, - SindexEntry -} from "../../../indexer" +import {IindexEntry, SimpleTxInput, SimpleUdEntryForWallet, SindexEntry} from "../../../indexer" export interface DividendEntry { pub: string member: boolean availables: number[] consumed: number[] - consumedUDs: { dividendNumber: number, dividend: { amount: number, base: number }Â }[] + consumedUDs: { + dividendNumber: number, + txHash: string, + txCreatedOn: string, + txLocktime: number, + dividend: { + amount: number, + base: number + } + }[] dividends: { amount: number, base: number }[] } @@ -44,4 +47,8 @@ export interface DividendDAO extends GenericDAO<DividendEntry> { getWrittenOnUDs(number: number): Promise<SimpleUdEntryForWallet[]> revertUDs(number: number): Promise<{ createdUDsDestroyedByRevert: SimpleUdEntryForWallet[], consumedUDsRecoveredByRevert: SimpleUdEntryForWallet[] }> + + findForDump(criterion: any): Promise<SindexEntry[]> + + trimConsumedUDs(belowNumber:number): Promise<void> } diff --git a/app/lib/dal/indexDAL/loki/LokiDividend.ts b/app/lib/dal/indexDAL/loki/LokiDividend.ts index 36d4d42f0a07c165b886e462e2174f28ae3b1cd5..daa96c8efdc0bc8a1232cf1778fe49464946760e 100644 --- a/app/lib/dal/indexDAL/loki/LokiDividend.ts +++ b/app/lib/dal/indexDAL/loki/LokiDividend.ts @@ -1,19 +1,10 @@ import {LokiIndex} from "./LokiIndex" import {DividendDAO, DividendEntry, UDSource} from "../abstract/DividendDAO" -import { - IindexEntry, - SimpleSindexEntryForWallet, - SimpleTxEntryForWallet, - SimpleTxInput, - SimpleUdEntryForWallet, - SindexEntry -} from "../../../indexer" +import {IindexEntry, 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']) } @@ -69,17 +60,22 @@ export class LokiDividend extends LokiIndex<DividendEntry> implements DividendDA for (const dividendToConsume of filter) { this.collection .chain() + // We look at the dividends of this member .find({ pub: dividendToConsume.identifier }) + // Then we try to consume the dividend being spent .update(m => { const index = m.availables.indexOf(dividendToConsume.pos) // We add it to the consumption history - m.consumed.push(dividendToConsume.writtenOn) + m.consumed.push(dividendToConsume.writtenOn) // `writtenOn` is the date (block#) of consumption m.consumedUDs.push({ dividendNumber: dividendToConsume.pos, - dividend: m.dividends[index] + dividend: m.dividends[index], + txCreatedOn: dividendToConsume.created_on as string, + txLocktime: dividendToConsume.locktime, + txHash: dividendToConsume.tx as string, }) // We remove it from available dividends @@ -240,4 +236,100 @@ export class LokiDividend extends LokiIndex<DividendEntry> implements DividendDA consumedUDsRecoveredByRevert, } } + + async findForDump(criterion: any): Promise<SindexEntry[]> { + const entries: SindexEntry[] = [] + const rows = await this.findRaw(criterion) + for (const m of rows) { + // Generate for unspent UDs + for (let i = 0; i < m.availables.length; i++) { + const writtenOn = m.availables[i] + const ud = m.dividends[i] + entries.push({ + op: 'CREATE', + index: 'SINDEX', + srcType: 'D', + tx: null, + identifier: m.pub, + writtenOn, + pos: writtenOn, + created_on: 'NULL', // TODO + written_on: writtenOn + '', // TODO + written_time: 0, // TODO + amount: ud.amount, + base: ud.base, + locktime: null as any, + consumed: false, + conditions: 'SIG(' + m.pub + ')', + unlock: null, + txObj: null as any, // TODO + age: 0, + }) + } + // Generate for spent UDs + for (let i = 0; i < m.consumed.length; i++) { + const writtenOn = m.consumed[i] + const ud = m.consumedUDs[i] + entries.push({ + op: 'CREATE', + index: 'SINDEX', + srcType: 'D', + tx: null, + identifier: m.pub, + writtenOn: ud.dividendNumber, + pos: ud.dividendNumber, + created_on: 'NULL', // TODO + written_on: writtenOn + '', // TODO + written_time: 0, // TODO + amount: ud.dividend.amount, + base: ud.dividend.base, + locktime: null as any, + consumed: false, + conditions: 'SIG(' + m.pub + ')', + unlock: null, + txObj: null as any, // TODO + age: 0, + }) + entries.push({ + op: 'UPDATE', + index: 'SINDEX', + srcType: 'D', + tx: ud.txHash, + identifier: m.pub, + writtenOn, + pos: ud.dividendNumber, + created_on: ud.txCreatedOn, + written_on: writtenOn + '', // TODO + written_time: 0, // TODO + amount: ud.dividend.amount, + base: ud.dividend.base, + locktime: ud.txLocktime, + consumed: true, + conditions: 'SIG(' + m.pub + ')', + unlock: null, + txObj: null as any, // TODO + age: 0, + }) + } + } + return entries + } + + async trimConsumedUDs(belowNumber: number): Promise<void> { + // Remove dividends consumed before `belowNumber` + this.collection + .chain() + .find({}) + .update(m => { + for (let i = 0; i < m.consumed.length; i++) { + const consumedBlockNumber = m.consumed[i] + if (consumedBlockNumber < belowNumber) { + // We trim this entry as it can't be reverted now + m.consumed.splice(i, 1) + m.consumedUDs.splice(i, 1) + i-- // The array changed, we loop back before i++ + } + } + }) + } } diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts index 4220688dc0ead66f3b8bd4ac85fbb8d634703d04..645c3078297430191cb9c11d0dcc33cc60e4a413 100644 --- a/app/lib/indexer.ts +++ b/app/lib/indexer.ts @@ -1723,7 +1723,6 @@ export class Indexer { index: 'SINDEX', op: 'UPDATE', srcType: 'T', - tx: null, identifier: src.identifier, pos: src.pos, amount: src.amount, @@ -1734,9 +1733,12 @@ export class Indexer { conditions: src.conditions, consumed: true, // It is now consumed - // TODO: make these fields not required using good types - created_on: '', - locktime: 0, + // TODO: change types to avoid casting + tx: (src as SindexEntry).tx, + created_on: (src as SindexEntry).created_on, + locktime: null as any, + + // TODO: make these fields being not required using good types unlock: null, txObj: {} as TransactionDTO, age: 0, diff --git a/app/modules/dump.ts b/app/modules/dump.ts index 5d3aae6907b2ea300c844246b5587bda46fc285e..ee5675d1c06277d00d78d9956691a2e7461fed1a 100644 --- a/app/modules/dump.ts +++ b/app/modules/dump.ts @@ -15,6 +15,7 @@ import {ConfDTO} from "../lib/dto/ConfDTO" import {Server} from "../../server" import {moment} from "../lib/common-libs/moment" import {DBBlock} from "../lib/db/DBBlock" +import {SindexEntry} from "../lib/indexer" const Table = require('cli-table') @@ -82,8 +83,11 @@ async function dumpTable(server: Server, name: string, condition?: string) { dump(rows, ['op','issuer','receiver','created_on','written_on','sig','expires_on','expired_on','chainable_on','from_wid','to_wid']) break case 's_index': - rows = await server.dal.sindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['identifier', false], ['pos', false]]) - dump(rows, ['op','tx','identifier','pos','created_on','written_on','written_time','amount','base','locktime','consumed','conditions']) + const rowsTX = await server.dal.sindexDAL.findRawWithOrder(criterion, [['writtenOn', false], ['identifier', false], ['pos', false]]) + const rowsUD = await server.dal.dividendDAL.findForDump(criterion) + rows = rowsTX.concat(rowsUD) + sortSindex(rows) + dump(rows, ['op','tx','identifier','pos','created_on','amount','base','locktime','consumed','conditions', 'writtenOn']) break default: console.error(`Unknown dump table ${name}`) @@ -158,4 +162,25 @@ async function getDateFor(server: Server, blockstamp: string) { function formatTimestamp(ts: number) { return moment(ts * 1000).format('YYYY-MM-DD hh:mm:ss') +} + +function sortSindex(rows: SindexEntry[]) { + // We sort by writtenOn, identifier, pos + rows.sort((a, b) => { + if (a.writtenOn === b.writtenOn) { + if (a.identifier === b.identifier) { + if (a.pos === b.pos) { + return a.op === 'CREATE' && b.op === 'UPDATE' ? -1 : (a.op === 'UPDATE' && b.op === 'CREATE' ? 1 : 0) + } else { + return a.pos < b.pos ? -1 : 1 + } + } + else { + return a.identifier < b.identifier ? -1 : 1 + } + } + else { + return a.writtenOn < b.writtenOn ? -1 : 1 + } + }) } \ No newline at end of file diff --git a/test/fast/protocol/protocol-brg106-number.ts b/test/fast/protocol/protocol-brg106-number.ts index cf3675c14ab0c9ad69d4e58208350d9d26e029ac..8fe0242a26f88d228b1320c55a5d2df492909cc9 100644 --- a/test/fast/protocol/protocol-brg106-number.ts +++ b/test/fast/protocol/protocol-brg106-number.ts @@ -67,28 +67,28 @@ describe("Protocol BR_G106 - Garbaging", function(){ 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(null); + cleaning[0].should.have.property('tx').equal(undefined); 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(null); + cleaning[1].should.have.property('tx').equal(undefined); 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(null); + cleaning[2].should.have.property('tx').equal(undefined); 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(null); + cleaning[3].should.have.property('tx').equal('B2'); cleaning[3].should.have.property('consumed').equal(true); cleaning[3].should.have.property('op').equal('UPDATE');