From add7d283a6162ca8073164d2199bf3339a45d294 Mon Sep 17 00:00:00 2001 From: Benoit Lavenier <benoit.lavenier@e-is.pro> Date: Tue, 30 May 2023 14:26:39 +0200 Subject: [PATCH] [fix] BMA: optimize `/tx/requirements/:search` for Duniter 1.8 - close #1439 --- app/lib/dal/fileDAL.ts | 37 ++++++++- app/lib/dal/indexDAL/abstract/IIndexDAO.ts | 2 + app/lib/dal/indexDAL/leveldb/LevelDBIindex.ts | 8 ++ app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts | 14 +++- app/lib/dal/sqliteDAL/IdentityDAL.ts | 4 + app/modules/bma/lib/controllers/wot.ts | 36 ++++++-- app/modules/bma/lib/parameters.ts | 10 +++ app/service/IdentityService.ts | 4 + test/integration/identity/identity-test.ts | 82 +++++++++++++++++++ 9 files changed, 188 insertions(+), 9 deletions(-) diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index a809685f3..585e0d2a6 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -707,6 +707,17 @@ export class FileDAL implements ServerDAO { return await this.iindexDAL.getFromPubkeyOrUid(search); } + async getWrittenIdtyByPubkeyForHashingAndIsMember( + pub: string + ): Promise<{ + uid: string; + created_on: string; + pub: string; + member: boolean; + } | null> { + return await this.iindexDAL.getFromPubkey(pub); + } + async getWrittenIdtyByPubkeyForRevocationCheck( pubkey: string ): Promise<{ @@ -866,8 +877,32 @@ export class FileDAL implements ServerDAO { return i; }) ); + return this.fillIdentitiesRevocation(found); + } + + async searchJustIdentitiesByPubkey(pubkey: string): Promise<DBIdentity[]> { + const pendings = await this.idtyDAL.findByPub(pubkey); + const writtenIdty = await this.iindexDAL.getOldFromPubkey(pubkey); + const nonPendings = + writtenIdty && + Underscore.where(pendings, { pubkey: writtenIdty.pub }).length === 0 + ? [writtenIdty] + : []; + const found = pendings.concat( + nonPendings.map((i: any) => { + // Use the correct field + i.pubkey = i.pub; + return i; + }) + ); + return this.fillIdentitiesRevocation(found); + } + + private async fillIdentitiesRevocation( + identities: DBIdentity[] + ): Promise<DBIdentity[]> { return await Promise.all<DBIdentity>( - found.map(async (f) => { + identities.map(async (f) => { const ms = await this.mindexDAL.getReducedMSForImplicitRevocation( f.pubkey ); diff --git a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts index 1c1a08cd8..e6ec8965a 100644 --- a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts +++ b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts @@ -19,6 +19,8 @@ export interface IIndexDAO extends ReduceableDAO<IindexEntry> { searchThoseMatching(search: string): Promise<OldIindexEntry[]>; + getOldFromPubkey(pub: string): Promise<OldIindexEntry | null>; + getFullFromUID(uid: string): Promise<FullIindexEntry>; getFullFromPubkey(pub: string): Promise<FullIindexEntry>; diff --git a/app/lib/dal/indexDAL/leveldb/LevelDBIindex.ts b/app/lib/dal/indexDAL/leveldb/LevelDBIindex.ts index c53f3dfc4..fd5010024 100644 --- a/app/lib/dal/indexDAL/leveldb/LevelDBIindex.ts +++ b/app/lib/dal/indexDAL/leveldb/LevelDBIindex.ts @@ -280,4 +280,12 @@ export class LevelDBIindex extends LevelDBTable<IindexEntry[]> .filter((u) => u.pub) .concat(pubIdentities.filter((p) => p.pub)); } + + async getOldFromPubkey(pub: string): Promise<OldIindexEntry | null> { + const identities = await this.findByPub(pub); + if (!identities.length) { + return null; + } + return OldTransformers.toOldIindexEntry(reduce(identities)); + } } diff --git a/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts b/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts index fdbadbdaf..0ad31596c 100644 --- a/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts +++ b/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts @@ -1,4 +1,4 @@ -import { FullIindexEntry, IindexEntry, Indexer } from "../../../indexer"; +import {FullIindexEntry, IindexEntry, Indexer, reduce} from "../../../indexer"; import { SQLiteDriver } from "../../drivers/SQLiteDriver"; import { MonitorExecutionTime } from "../../../debug/MonitorExecutionTime"; import { IIndexDAO } from "../abstract/IIndexDAO"; @@ -212,6 +212,18 @@ export class SqliteIIndex extends SqliteTable<IindexEntry> return (await this.getFromUID(uid)) as FullIindexEntry; } + @MonitorExecutionTime() + async getOldFromPubkey(pub: string): Promise<OldIindexEntry | null> { + const entries = await this.find( + "SELECT * FROM iindex WHERE pub = ? order by writtenOn ASC", + [pub] + ); + if (!entries.length) { + return null; + } + return OldTransformers.toOldIindexEntry(reduce(entries)); + } + @MonitorExecutionTime() async getMembers(): Promise<{ pubkey: string; uid: string | null }[]> { const members = await this.find( diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.ts b/app/lib/dal/sqliteDAL/IdentityDAL.ts index 96d602a0a..851b27c0d 100644 --- a/app/lib/dal/sqliteDAL/IdentityDAL.ts +++ b/app/lib/dal/sqliteDAL/IdentityDAL.ts @@ -328,6 +328,10 @@ export class IdentityDAL extends AbstractSQLite<DBIdentity> { }); } + findByPub(pub: string) { + return this.sqlFind({ pubkey: pub }); + } + async trimExpiredIdentities(medianTime: number) { await this.exec( "DELETE FROM " + diff --git a/app/modules/bma/lib/controllers/wot.ts b/app/modules/bma/lib/controllers/wot.ts index 61e869b06..73422c518 100644 --- a/app/modules/bma/lib/controllers/wot.ts +++ b/app/modules/bma/lib/controllers/wot.ts @@ -132,9 +132,16 @@ export class WOTBinding extends AbstractController { async certifiersOf(req: any): Promise<HttpCertifications> { const search = await ParametersService.getSearchP(req); - const idty = (await this.server.dal.getWrittenIdtyByPubkeyOrUIdForHashingAndIsMember( - search - )) as FullIindexEntry; + let idty: FullIindexEntry; + if (ParametersService.getIsPubkey(req)) { + idty = (await this.server.dal.getWrittenIdtyByPubkeyForHashingAndIsMember( + search + )) as FullIindexEntry; + } else { + idty = (await this.server.dal.getWrittenIdtyByPubkeyOrUIdForHashingAndIsMember( + search + )) as FullIindexEntry; + } const certs = await this.server.dal.certsToTarget( idty.pub, IdentityDTO.getTargetHash(idty) @@ -180,7 +187,15 @@ export class WOTBinding extends AbstractController { async requirements(req: any): Promise<HttpRequirements> { const search = await ParametersService.getSearchP(req); - const identities: any = await this.IdentityService.searchIdentities(search); + let identities: any = []; + if (ParametersService.getIsPubkey(req)) { + if (!BMAConstants.PUBLIC_KEY.test(search)) { + throw BMAConstants.ERRORS.NO_IDTY_MATCHING_PUB_OR_UID; + } + identities = await this.IdentityService.searchIdentitiesByPubkey(search); + } else { + identities = await this.IdentityService.searchIdentities(search); + } const all: HttpIdentityRequirement[] = await this.BlockchainService.requirementsOfIdentities( identities ); @@ -229,9 +244,16 @@ export class WOTBinding extends AbstractController { async certifiedBy(req: any): Promise<HttpCertifications> { const search = await ParametersService.getSearchP(req); - const idty = (await this.server.dal.getWrittenIdtyByPubkeyOrUIdForHashingAndIsMember( - search - )) as FullIindexEntry; + let idty: FullIindexEntry; + if (ParametersService.getIsPubkey(req)) { + idty = (await this.server.dal.getWrittenIdtyByPubkeyForHashingAndIsMember( + search + )) as FullIindexEntry; + } else { + idty = (await this.server.dal.getWrittenIdtyByPubkeyOrUIdForHashingAndIsMember( + search + )) as FullIindexEntry; + } const certs = await this.server.dal.certsFrom(idty.pub); const theCerts: HttpCertification[] = []; for (const cert of certs) { diff --git a/app/modules/bma/lib/parameters.ts b/app/modules/bma/lib/parameters.ts index 49a437f26..c5463ecd9 100644 --- a/app/modules/bma/lib/parameters.ts +++ b/app/modules/bma/lib/parameters.ts @@ -96,6 +96,16 @@ export class ParametersService { callback(null, matches[0]); }; + static getIsPubkey(req: any): boolean { + const value = req.query.pubkey; + return ( + value !== null && + value !== undefined && + value !== "false" && + value !== false + ); + } + static getPubkeyP(req: any) { return Q.nbind(ParametersService.getPubkey, this)(req); } diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts index 676b8602a..f3173f885 100644 --- a/app/service/IdentityService.ts +++ b/app/service/IdentityService.ts @@ -54,6 +54,10 @@ export class IdentityService extends FIFOService { return this.dal.searchJustIdentities(search); } + searchIdentitiesByPubkey(pubkey: string) { + return this.dal.searchJustIdentitiesByPubkey(pubkey); + } + async findMember(search: string) { let idty = null; if (search.match(constants.PUBLIC_KEY)) { diff --git a/test/integration/identity/identity-test.ts b/test/integration/identity/identity-test.ts index 6966e9971..2a60a7ffb 100644 --- a/test/integration/identity/identity-test.ts +++ b/test/integration/identity/identity-test.ts @@ -192,6 +192,13 @@ describe("Identities collision", function() { }); }); + it('should have certifiers-of/:pubkey of cat giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certifiers-of/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); + res.should.have.property('uid').equal('cat'); + }); + }); + it('should have certifiers-of/tic giving results', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/certifiers-of/tic', { json: true }), function(res:HttpCertifications) { res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); @@ -213,6 +220,13 @@ describe("Identities collision", function() { }); }); + it('should have certifiers-of/:pubkey of tic giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certifiers-of/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); + res.should.have.property('uid').equal('tic'); + }); + }); + it('should have certifiers-of/toc giving results', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/certifiers-of/toc', { json: true }), function(res:HttpCertifications) { res.should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'); @@ -234,6 +248,13 @@ describe("Identities collision", function() { }); }); + it('should have certifiers-of/:pubkey of toc giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certifiers-of/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo'); + res.should.have.property('uid').equal('toc'); + }); + }); + it('requirements of cat', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/requirements/cat', { json: true }), function(res:HttpRequirements) { res.should.have.property('identities').be.an.Array; @@ -270,6 +291,46 @@ describe("Identities collision", function() { }); }); + it('requirements by pubkey of cat', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/requirements/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd?pubkey', { json: true }), function(res:HttpRequirements) { + res.should.have.property('identities').be.an.Array; + res.should.have.property('identities').have.length(1); + res.identities[0].should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); + res.identities[0].should.have.property('uid').equal('cat'); + res.identities[0].should.have.property('meta').property('timestamp'); + res.identities[0].should.have.property('wasMember').equal(true); + res.identities[0].should.have.property('expired').equal(false); // Because it has been a member once! So its identity will exist forever. + res.identities[0].should.have.property('outdistanced').equal(false); + res.identities[0].should.have.property('isSentry').equal(true); // dSen = 2, cat has issued and received 2 certs with tic and toc + res.identities[0].should.have.property('certifications').have.length(2); + res.identities[0].should.have.property('membershipPendingExpiresIn').equal(0); + res.identities[0].should.have.property('membershipExpiresIn').greaterThan(9000); + }); + }); + + it('requirements by pubkey of man1', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/requirements/12AbjvYY5hxV4v2KrN9pnGzgFxogwrzgYyncYHHsyFDK?pubkey', { json: true }), function(res:HttpRequirements) { + res.should.have.property('identities').be.an.Array; + res.should.have.property('identities').have.length(1); + res.identities[0].should.have.property('pubkey').equal('12AbjvYY5hxV4v2KrN9pnGzgFxogwrzgYyncYHHsyFDK'); + res.identities[0].should.have.property('uid').equal('man1'); + res.identities[0].should.have.property('meta').property('timestamp'); + res.identities[0].should.have.property('expired').equal(false); + res.identities[0].should.have.property('outdistanced').equal(false); + res.identities[0].should.have.property('isSentry').equal(false); // Not a member, also dSen = 2, but man1 has only 1 certification + res.identities[0].should.have.property('certifications').length(1); + res.identities[0].certifications[0].should.have.property('from').equal('2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc'); + res.identities[0].certifications[0].should.have.property('to').equal('12AbjvYY5hxV4v2KrN9pnGzgFxogwrzgYyncYHHsyFDK'); + res.identities[0].certifications[0].should.have.property('expiresIn').greaterThan(0); + res.identities[0].should.have.property('membershipPendingExpiresIn').greaterThan(9000); + res.identities[0].should.have.property('membershipExpiresIn').equal(0); + }); + }); + + it('requirements by invalid pubkey', function() { + return expectError(404, "No identity matching this pubkey or uid", rp('http://127.0.0.1:7799/wot/requirements/cat?pubkey', { json: true })); + }); + it('should have certified-by/tic giving results', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/tic', { json: true }), function(res:HttpCertifications) { res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); @@ -298,6 +359,13 @@ describe("Identities collision", function() { }); }); + it('should have certified-by/:pubkey of tic giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV'); + res.should.have.property('uid').equal('tic'); + }); + }); + it('should have certified-by/tac giving results', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/tac', { json: true }), function(res:HttpCertifications) { res.should.have.property('pubkey').equal('2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc'); @@ -308,6 +376,13 @@ describe("Identities collision", function() { }); }); + it('should have certified-by/:pubkey of tac giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc'); + res.should.have.property('uid').equal('tac'); + }); + }); + it('should have certified-by/cat giving results', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/cat', { json: true }), function(res:HttpCertifications) { res.should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); @@ -343,6 +418,13 @@ describe("Identities collision", function() { }); }); + it('should have certified-by/:pubkey of cat giving results', function() { + return expectAnswer(rp('http://127.0.0.1:7799/wot/certified-by/HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd?pubkey', { json: true }), function(res:HttpCertifications) { + res.should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'); + res.should.have.property('uid').equal('cat'); + }); + }); + it('requirements of man2', function() { return expectAnswer(rp('http://127.0.0.1:7799/wot/requirements/man2', { json: true }), function(res:HttpRequirements) { res.should.have.property('identities').be.an.Array; -- GitLab