diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts index 57c9eb9887c2c301cf773469d40ea21782df2fcd..4d9ef79e7ca37b3c3fe11047e76b58d4dcda6a70 100644 --- a/app/lib/dal/fileDAL.ts +++ b/app/lib/dal/fileDAL.ts @@ -43,7 +43,7 @@ import { MetaDAL } from "./sqliteDAL/MetaDAL"; import { DataErrors } from "../common-libs/errors"; import { BasicRevocableIdentity, IdentityDTO } from "../dto/IdentityDTO"; import { FileSystem } from "../system/directory"; -import { RustDbTx, RustServer, RustServerConf, Wot } from "../../../neon/lib"; +import { RustDbTx, RustServer, Wot } from "../../../neon/lib"; import { IIndexDAO } from "./indexDAL/abstract/IIndexDAO"; import { BIndexDAO } from "./indexDAL/abstract/BIndexDAO"; import { MIndexDAO } from "./indexDAL/abstract/MIndexDAO"; @@ -929,8 +929,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 ); @@ -1705,7 +1729,7 @@ export class FileDAL implements ServerDAO { local_iindex: IindexEntry[] ): Promise<SimpleUdEntryForWallet[]> { if (dividend) { - let udSources = this.dividendDAL.produceDividend( + const udSources = this.dividendDAL.produceDividend( blockNumber, dividend, unitbase, diff --git a/app/lib/dal/indexDAL/abstract/IIndexDAO.ts b/app/lib/dal/indexDAL/abstract/IIndexDAO.ts index 1c1a08cd8052ec1a003e478b563ea2ef5562dc15..e6ec8965ae19f0848afb1af2ebbde887d27caec6 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 c53f3dfc48a11b38ca1e4d721a5d9ef17a59e0c8..fd5010024ec958c2e6768c2ac5de27427f3c9ba1 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 fdbadbdaf23fdb168c1bfa76fee797d7e226e7b4..03bd59df9682b697ffbef37845492dc83a889ed9 100644 --- a/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts +++ b/app/lib/dal/indexDAL/sqlite/SqliteIIndex.ts @@ -1,4 +1,9 @@ -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 +217,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 96d602a0af9da5b84031716ff0f3d18bcb54938b..851b27c0d7b0c7eb6dca672d88e6c8bb1d3e28df 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 17e57ed83629f8c545e6f5d05a68a1fdce4b48de..73422c518b6fe41820b115de7237280ecae6a613 100644 --- a/app/modules/bma/lib/controllers/wot.ts +++ b/app/modules/bma/lib/controllers/wot.ts @@ -133,7 +133,7 @@ export class WOTBinding extends AbstractController { async certifiersOf(req: any): Promise<HttpCertifications> { const search = await ParametersService.getSearchP(req); let idty: FullIindexEntry; - if (req.query.pubkey) { + if (ParametersService.getIsPubkey(req)) { idty = (await this.server.dal.getWrittenIdtyByPubkeyForHashingAndIsMember( search )) as FullIindexEntry; @@ -187,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 ); @@ -237,7 +245,7 @@ export class WOTBinding extends AbstractController { async certifiedBy(req: any): Promise<HttpCertifications> { const search = await ParametersService.getSearchP(req); let idty: FullIindexEntry; - if (req.query.pubkey) { + if (ParametersService.getIsPubkey(req)) { idty = (await this.server.dal.getWrittenIdtyByPubkeyForHashingAndIsMember( search )) as FullIindexEntry; diff --git a/app/modules/bma/lib/parameters.ts b/app/modules/bma/lib/parameters.ts index 49a437f26c53ad1e00efcd9146f2bdcea021c730..c5463ecd963be1655785292a448d83d6f42179d0 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 676b8602a57b183a8582444c9be37e77ca39cd12..f3173f885c8f1adc9595879298f1798ef10d52f7 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/doc/HTTP_API.md b/doc/HTTP_API.md index 3a5bace82210065faff189686f6cd51b56108b80..a6411a092d67d4353bc8c3a149a2bde05604641c 100644 --- a/doc/HTTP_API.md +++ b/doc/HTTP_API.md @@ -162,10 +162,10 @@ For that purpose, Merkle URL defines different parameters and results: **Parameters** -Parameter | Description ---------- | ----------- -`leaves` | Defines wether or not leaves hashes should be returned too. Defaults to `false`. -`leaf` | Hash of a leaf whose content should be returned. Ignore `leaves` parameter. +| Parameter | Description | Method | +|-----------|----------------------------------------------------------------------------------|-------------| +| `leaves` | Defines wether or not leaves hashes should be returned too. Defaults to `false`. | Query param | +| `leaf` | Hash of a leaf whose content should be returned. Ignore `leaves` parameter. | Query param | **Returns** @@ -216,9 +216,9 @@ Each tree manages different data, and has a different goal. Hence, each tree has Here is a summup of such rules: -Merkle URL | Leaf | Sort ----------------------- | --------------------------| ------------- -`network/peers (GET)` | Hash of the peers' pubkey | By hash string sort, ascending. +| Merkle URL | Leaf | Sort | +|------------------------| --------------------------|---------------------------------| +| `network/peers (GET)` | Hash of the peers' pubkey | By hash string sort, ascending. | #### Unicity @@ -290,9 +290,9 @@ POST [Identity](./Protocol.md#identity) data. **Parameters** -Name | Value | Method ----- | ----- | ------ -`identity` | The raw identity. | POST +| Name | Value | Method | +|------------|-------------------|---------| +| `identity` | The raw identity. | POST | **Returns** @@ -330,9 +330,9 @@ POST [Certification](./Protocol.md#certification) data. **Parameters** -Name | Value | Method ----- | ----- | ------ -`cert` | The raw certification. | POST +| Name | Value | Method | +|--------|------------------------|---------| +| `cert` | The raw certification. | POST | **Returns** @@ -362,9 +362,9 @@ Remove an identity from Identity pool. **Parameters** -Name | Value | Method ----- | ----- | ------ -`revocation` | The raw revocation. | POST +| Name | Value | Method | +|--------------|---------------------|--------| +| `revocation` | The raw revocation. | POST | **Returns** @@ -384,9 +384,9 @@ GET [Public key](./Protocol.md#publickey) data. **Parameters** -Name | Value | Method ----- | ----- | ------ -`search` | A string of data to look for (public key, uid). | URL +| Name | Value | Method | +|----------|-------------------------------------------------|---------| +| `search` | A string of data to look for (public key, uid). | URL | **Returns** @@ -488,7 +488,7 @@ A list of public key + uid. } ``` -#### `wot/requirements/[pubkey]` +#### `wot/requirements/[search]` **Goal** @@ -497,9 +497,10 @@ GET requirements to be filled by pubkey to become a member. **Parameters** -Name | Value | Method ----- | ----- | ------ -`pbkey` | Public key to check. | URL +| Name | Value | Method | +|----------|-----------------------------------------------------------------------------------------------------------------|--------------| +| `search` | Public key or uidto check. | URL | +| `pubkey` | Defines wether or not `search` is a pubkey. It not, will search on both `uid` or `pubkey`. Defaults to `false`. | Query params | **Returns** @@ -543,9 +544,9 @@ GET requirements pending on the server for identities with a minimum of minsig c **Parameters** -Name | Value | Method ----- | ----- | ------ -`minsig` | Return only pending identities with a minimum of minsig certifications | URL +| Name | Value | Method | +|----------|------------------------------------------------------------------------|--------| +| `minsig` | Return only pending identities with a minimum of minsig certifications | URL | **Returns** @@ -637,9 +638,9 @@ GET [Certification](./Protocol.md#certification) data over a member. **Parameters** -Name | Value | Method ----- | ----- | ------ -`search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL +| Name | Value | Method | +|----------|-------------------------------------------------------------------------------------------------|--------| +| `search` | Public key or uid of a *member* (or someone who *was a member*) we want see the certifications. | URL | **Returns** @@ -685,9 +686,9 @@ GET identity data written for a member. **Parameters** -Name | Value | Method ----- | ----- | ------ -`search` | Public key or uid of a *member* we want see the attached identity. | URL +| Name | Value | Method | +|----------|--------------------------------------------------------------------|--------| +| `search` | Public key or uid of a *member* we want see the attached identity. | URL | **Returns** @@ -752,9 +753,9 @@ POST a [Membership](./Protocol.md#membership) document. **Parameters** -Name | Value | Method ----- | ----- | ------ -`membership` | The membership document (with signature). | POST +| Name | Value | Method | +|--------------|-------------------------------------------|--------| +| `membership` | The membership document (with signature). | POST | **Returns** @@ -816,10 +817,10 @@ POST a new block to add to the blockchain. **Parameters** -Name | Value | Method ------------------- | ------------------------------ | ------ -`block` | The raw block to be added | POST -`signature` | Signature of the raw block | POST +| Name | Value | Method | +|--------------------|---------------------------------|--------| +| `block` | The raw block to be added | POST | +| `signature` | Signature of the raw block | POST | **Returns** @@ -833,9 +834,9 @@ GET the promoted block whose number `NUMBER`. **Parameters** -Name | Value | Method ------------------- | ------------------------------------------------------------- | ------ -`NUMBER` | The promoted block number (integer value) we want to see. | URL +| Name | Value | Method | +|--------------------|----------------------------------------------------------------|---------| +| `NUMBER` | The promoted block number (integer value) we want to see. | URL | **Returns** @@ -923,10 +924,10 @@ GET the `[COUNT]` promoted blocks from `[FROM]` number, inclusive. **Parameters** -Name | Value | Method ------------------- | ------------------------------------------------------------- | ------ -`COUNT` | The number of blocks we want to see. | URL -`FROM` | The starting block. | URL +| Name | Value | Method | +|---------|--------------------------------------|---------| +| `COUNT` | The number of blocks we want to see. | URL | +| `FROM` | The starting block. | URL | **Returns** @@ -952,9 +953,9 @@ GET hardship level for given member's pubkey for writing next block. **Parameters** -Name | Value | Method ----- | ----- | ------ -`PUBKEY` | Member's pubkey. | URL +| Name | Value | Method | +|----------|------------------------------|--------| +| `PUBKEY` | Member's pubkey. | URL | **Returns** @@ -1281,7 +1282,12 @@ Merkle URL refering to peering entries of every node inside the currency network **Parameters** -*None*. +**Parameters** + +| Parameter | Description | Method | +|-----------|----------------------------------------------------------------------------------|-------------| +| `leaves` | Defines wether or not leaves hashes should be returned too. Defaults to `false`. | Query param | +| `leaf` | Hash of a leaf whose content should be returned. Ignore `leaves` parameter. | Query param | **Returns** @@ -1320,9 +1326,9 @@ POST a peering entry document. **Parameters** -Name | Value | Method ------------ | ----------------------------------- | ------ -`peer` | The peering entry document. | POST +| Name | Value | Method | +|-------------|-----------------------------|--------| +| `peer` | The peering entry document. | POST | **Returns** @@ -1382,9 +1388,9 @@ POST a transaction. **Parameters** -Name | Value | Method ------------------ | ------------------------------------------------------------- | ------ -`transaction` | The raw transaction. | POST +| Name | Value | Method | +|---------------|----------------------|--------| +| `transaction` | The raw transaction. | POST | **Returns** @@ -1440,9 +1446,9 @@ GET a list of available sources. **Parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Owner of the coins' pubkey. | URL +| Name | Value | Method | +|----------|------------------------------|--------| +| `pubkey` | Owner of the coins' pubkey. | URL | **Returns** @@ -1483,9 +1489,9 @@ Get the wallet transaction history **parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Wallet public key. | URL +| Name | Value | Method | +|----------|--------------------|----------| +| `pubkey` | Wallet public key. | URL | **Returns** @@ -1616,9 +1622,9 @@ Get the wallet transaction pending history **parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Wallet public key. | URL +| Name | Value | Method | +|----------|--------------------|--------| +| `pubkey` | Wallet public key. | URL | **Returns** @@ -1709,11 +1715,11 @@ Get the wallet transaction history **parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Wallet public key. | URL -`from` | The starting block. | URL -`to` | the ending block. | URL +| Name | Value | Method | +|----------|---------------------|---------| +| `pubkey` | Wallet public key. | URL | +| `from` | The starting block. | URL | +| `to` | the ending block. | URL | **Returns** @@ -1839,11 +1845,11 @@ Get the wallet transaction history **parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Wallet public key. | URL -`from` | The starting timestamp limit. (optionnal) | URL -`to` | The ending timestamp. (optionnal) | URL +| Name | Value | Method | +|----------|-------------------------------------------|---------| +| `pubkey` | Wallet public key. | URL | +| `from` | The starting timestamp limit. (optionnal) | URL | +| `to` | The ending timestamp. (optionnal) | URL | **Returns** @@ -1970,9 +1976,9 @@ Get the wallet universal dividend history (only not consumed ones) **parameters** -Name | Value | Method ----- | ----- | ------ -`pubkey` | Wallet public key. | URL +| Name | Value | Method | +|----------|---------------------|--------| +| `pubkey` | Wallet public key. | URL | **Returns** diff --git a/test/integration/identity/identity-test.ts b/test/integration/identity/identity-test.ts index 847ff19e8e20b6d0e86c7cd36e6ad1591068e76e..2a60a7ffb6f58ba0fcf6c4a4eef072101ec022a7 100644 --- a/test/integration/identity/identity-test.ts +++ b/test/integration/identity/identity-test.ts @@ -291,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');