Skip to content
Snippets Groups Projects
Commit d6eb97ca authored by Benoit Lavenier's avatar Benoit Lavenier
Browse files

[fix] Report changes from merge request !1420 for release/1.8 - close #1438

parent 813d06c3
Branches
Tags
1 merge request!1428fix(1438): Optimize BMA /tx/sources
Pipeline #32222 failed
...@@ -53,7 +53,7 @@ const CONDITIONS = ...@@ -53,7 +53,7 @@ const CONDITIONS =
"\\)|CSV\\(" + "\\)|CSV\\(" +
CSV_INTEGER + CSV_INTEGER +
"\\))))*"; "\\))))*";
const CONDITION_SIG_PUBKEY = "SIG\\((" + PUBKEY + ")\\)";
const BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/; const BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
const BMAS_REGEXP = /^BMAS( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))( (\/.+))?$/; const BMAS_REGEXP = /^BMAS( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))( (\/.+))?$/;
const BMATOR_REGEXP = /^BMATOR( ([a-z0-9]{16})\.onion)( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/; const BMATOR_REGEXP = /^BMATOR( ([a-z0-9]{16})\.onion)( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
...@@ -533,6 +533,8 @@ export const CommonConstants = { ...@@ -533,6 +533,8 @@ export const CommonConstants = {
LOCKTIME: find("Locktime: (" + INTEGER + ")"), LOCKTIME: find("Locktime: (" + INTEGER + ")"),
INLINE_COMMENT: exact(COMMENT), INLINE_COMMENT: exact(COMMENT),
OUTPUT_CONDITION: exact(CONDITIONS), OUTPUT_CONDITION: exact(CONDITIONS),
OUTPUT_CONDITION_SIG_PUBKEY: find(CONDITION_SIG_PUBKEY),
OUTPUT_CONDITION_SIG_PUBKEY_UNIQUE: exact(CONDITION_SIG_PUBKEY),
}, },
PEER: { PEER: {
BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"), BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"),
......
...@@ -12,12 +12,14 @@ import { SIndexDAO } from "../abstract/SIndexDAO"; ...@@ -12,12 +12,14 @@ import { SIndexDAO } from "../abstract/SIndexDAO";
import { Underscore } from "../../../common-libs/underscore"; import { Underscore } from "../../../common-libs/underscore";
import { pint } from "../../../common-libs/pint"; import { pint } from "../../../common-libs/pint";
import { arrayPruneAllCopy } from "../../../common-libs/array-prune"; import { arrayPruneAllCopy } from "../../../common-libs/array-prune";
import { CommonConstants } from "../../../common-libs/constants";
export class LevelDBSindex extends LevelDBTable<SindexEntry> export class LevelDBSindex extends LevelDBTable<SindexEntry>
implements SIndexDAO { implements SIndexDAO {
private indexForTrimming: LevelDBTable<string[]>; private indexForTrimming: LevelDBTable<string[]>;
private indexForConsumed: LevelDBTable<string[]>; private indexForConsumed: LevelDBTable<string[]>;
private indexForConditions: LevelDBTable<string[]>; private indexForConditions: LevelDBTable<string[]>;
private indexOfComplexeConditionForPubkeys: LevelDBTable<string[]>;
constructor(protected getLevelDB: (dbName: string) => Promise<LevelUp>) { constructor(protected getLevelDB: (dbName: string) => Promise<LevelUp>) {
super("level_sindex", getLevelDB); super("level_sindex", getLevelDB);
...@@ -41,9 +43,14 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -41,9 +43,14 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
"level_sindex/conditions", "level_sindex/conditions",
this.getLevelDB this.getLevelDB
); );
this.indexOfComplexeConditionForPubkeys = new LevelDBTable<string[]>(
"level_sindex/complex_condition_pubkeys",
this.getLevelDB
);
await this.indexForTrimming.init(); await this.indexForTrimming.init();
await this.indexForConsumed.init(); await this.indexForConsumed.init();
await this.indexForConditions.init(); await this.indexForConditions.init();
await this.indexOfComplexeConditionForPubkeys.init();
} }
async close(): Promise<void> { async close(): Promise<void> {
...@@ -51,6 +58,7 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -51,6 +58,7 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
await this.indexForTrimming.close(); await this.indexForTrimming.close();
await this.indexForConsumed.close(); await this.indexForConsumed.close();
await this.indexForConditions.close(); await this.indexForConditions.close();
await this.indexOfComplexeConditionForPubkeys.close();
} }
/** /**
...@@ -127,14 +135,14 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -127,14 +135,14 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
pos: number; pos: number;
}[] }[]
> { > {
// TODO: very costly: needs a full scan, would be better to change this implementatio const forSimpleConditions = await this.getForConditions(`SIG(${pubkey})`);
const entries = await this.findWhere((e) => const forComplexConditions = await this.getForComplexeConditionPubkey(
e.conditions.includes(`SIG(${pubkey})`) pubkey
);
const reduced = Indexer.DUP_HELPERS.reduceBy(
forSimpleConditions.concat(forComplexConditions),
["identifier", "pos"]
); );
const reduced = Indexer.DUP_HELPERS.reduceBy(entries, [
"identifier",
"pos",
]);
return reduced.filter((r) => !r.consumed); return reduced.filter((r) => !r.consumed);
} }
...@@ -269,6 +277,20 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -269,6 +277,20 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
return found; return found;
} }
async getForComplexeConditionPubkey(pubkey: string): Promise<SindexEntry[]> {
const ids =
(await this.indexOfComplexeConditionForPubkeys.getOrNull(pubkey)) || [];
const found: SindexEntry[] = [];
for (const id of ids) {
const entries = await this.findByIdentifierAndPos(
id.split("-")[0],
pint(id.split("-")[1])
);
entries.forEach((e) => found.push(e));
}
return found;
}
async removeBlock(blockstamp: string): Promise<void> { async removeBlock(blockstamp: string): Promise<void> {
const writtenOn = pint(blockstamp); const writtenOn = pint(blockstamp);
// We look at records written on this blockstamp: `indexForTrimming` allows to get them // We look at records written on this blockstamp: `indexForTrimming` allows to get them
...@@ -316,24 +338,25 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -316,24 +338,25 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
} }
private async trimConditions(condition: string, id: string) { private async trimConditions(condition: string, id: string) {
// Get all the account's TX sources // Get all the condition's sources
const existing = (await this.indexForConditions.getOrNull(condition)) || []; const existing = (await this.indexForConditions.getOrNull(condition)) || [];
// Prune the source from the account // Prune the source from the condition
const trimmed = arrayPruneAllCopy(existing, id); const trimmed = arrayPruneAllCopy(existing, id);
if (trimmed.length) { if (trimmed.length) {
// If some sources are left for this "account", persist what remains // If some sources are left for this "condition", persist what remains
await this.indexForConditions.put(condition, trimmed); await this.indexForConditions.put(condition, trimmed);
} else { } else {
// Otherwise just delete the "account" // Otherwise just delete the "account"
await this.indexForConditions.del(condition); await this.indexForConditions.del(condition);
} }
// If complex conditions
if (this.isComplexCondition(condition)) {
const pubkeys = this.getDistinctPubkeysFromCondition(condition);
await this.trimComplexeConditionPubkeys(pubkeys, id);
}
} }
/**
* Duplicate with trimConditions?!
* @param writtenOn
* @param id
*/
private async trimWrittenOn(writtenOn: number, id: string) { private async trimWrittenOn(writtenOn: number, id: string) {
const k = LevelDBSindex.trimWrittenOnKey(writtenOn); const k = LevelDBSindex.trimWrittenOnKey(writtenOn);
const existing = await this.getWrittenOnSourceIds(writtenOn); const existing = await this.getWrittenOnSourceIds(writtenOn);
...@@ -356,6 +379,28 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -356,6 +379,28 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
} }
} }
private async trimComplexeConditionPubkeys(pubkeys: string[], id: string) {
if (!pubkeys || !pubkeys.length) return;
for (const p of pubkeys) {
await this.trimComplexeConditionPubkey(p, id);
}
}
private async trimComplexeConditionPubkey(pubkey: string, id: string) {
// Get all the condition's sources
const existing =
(await this.indexOfComplexeConditionForPubkeys.getOrNull(pubkey)) || [];
// Prune the source from the condition
const trimmed = arrayPruneAllCopy(existing, id);
if (trimmed.length) {
// If some sources are left for this "condition", persist what remains
await this.indexOfComplexeConditionForPubkeys.put(pubkey, trimmed);
} else {
// Otherwise just delete the "account"
await this.indexOfComplexeConditionForPubkeys.del(pubkey);
}
}
private async getWrittenOnSourceIds(writtenOn: number) { private async getWrittenOnSourceIds(writtenOn: number) {
const indexForTrimmingId = LevelDBSindex.trimWrittenOnKey(writtenOn); const indexForTrimmingId = LevelDBSindex.trimWrittenOnKey(writtenOn);
return (await this.indexForTrimming.getOrNull(indexForTrimmingId)) || []; return (await this.indexForTrimming.getOrNull(indexForTrimmingId)) || [];
...@@ -393,6 +438,7 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -393,6 +438,7 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
const byConsumed: { [k: number]: SindexEntry[] } = {}; const byConsumed: { [k: number]: SindexEntry[] } = {};
const byWrittenOn: { [k: number]: SindexEntry[] } = {}; const byWrittenOn: { [k: number]: SindexEntry[] } = {};
const byConditions: { [k: string]: SindexEntry[] } = {}; const byConditions: { [k: string]: SindexEntry[] } = {};
const byPubkeys: { [k: string]: SindexEntry[] } = {};
records records
.filter((r) => r.consumed) .filter((r) => r.consumed)
.forEach((r) => { .forEach((r) => {
...@@ -410,12 +456,24 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -410,12 +456,24 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
arrWO = byWrittenOn[r.writtenOn] = []; arrWO = byWrittenOn[r.writtenOn] = [];
} }
arrWO.push(r); arrWO.push(r);
// Conditiosn // Conditions
let arrCN = byConditions[r.conditions]; let arrCN = byConditions[r.conditions];
if (!arrCN) { if (!arrCN) {
arrCN = byConditions[r.conditions] = []; arrCN = byConditions[r.conditions] = [];
} }
arrCN.push(r); arrCN.push(r);
// If complex condition
if (this.isComplexCondition(r.conditions)) {
const pubkeys = this.getDistinctPubkeysFromCondition(r.conditions);
pubkeys.forEach((pub) => {
let arrPub = byPubkeys[pub];
if (!arrPub) {
arrPub = byPubkeys[pub] = [];
}
arrPub.push(r);
});
}
}); });
// Index consumed => (identifier + pos)[] // Index consumed => (identifier + pos)[]
for (const k of Underscore.keys(byConsumed)) { for (const k of Underscore.keys(byConsumed)) {
...@@ -446,5 +504,47 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry> ...@@ -446,5 +504,47 @@ export class LevelDBSindex extends LevelDBTable<SindexEntry>
Underscore.uniq(existing.concat(newSources)) Underscore.uniq(existing.concat(newSources))
); );
} }
// Index pubkeys => (identifier + pos)[]
for (const k of Underscore.keys(byPubkeys).map(String)) {
const existing =
(await this.indexOfComplexeConditionForPubkeys.getOrNull(k)) || [];
const newSources = byPubkeys[k].map((r) =>
LevelDBSindex.trimPartialKey(r.identifier, r.pos)
);
await this.indexOfComplexeConditionForPubkeys.put(
k,
Underscore.uniq(existing.concat(newSources))
);
}
}
private isComplexCondition(condition: string): boolean {
return (
(condition &&
!CommonConstants.TRANSACTION.OUTPUT_CONDITION_SIG_PUBKEY_UNIQUE.test(
condition
)) ||
false
);
}
/**
* Get all pubkeys used by an output condition (e.g. 'SIG(A) && SIG(B)' will return ['A', 'B']
* @param condition
* @private
*/
private getDistinctPubkeysFromCondition(condition: string): string[] {
const pubKeys: string[] = [];
if (!condition) return pubKeys;
let match: RegExpExecArray | null;
while (
(match = CommonConstants.TRANSACTION.OUTPUT_CONDITION_SIG_PUBKEY.exec(
condition
)) !== null
) {
pubKeys.push(match[1]);
condition = condition.substring(match.index + match[0].length);
}
return Underscore.uniq(pubKeys);
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment