diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79ea0e932..668997931 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,11 +7,11 @@ concurrency: on: pull_request: branches: - - 'master' + - 'main' - 'release/*' push: branches: - - 'master' + - 'main' - 'release/*' diff --git a/integration-tests/e2e-tests/features/provide_anoncred_proof.feature b/integration-tests/e2e-tests/features/provide_anoncred_proof.feature index a50545535..6170455f6 100644 --- a/integration-tests/e2e-tests/features/provide_anoncred_proof.feature +++ b/integration-tests/e2e-tests/features/provide_anoncred_proof.feature @@ -4,7 +4,7 @@ Feature: Provide anoncred proof Scenario: Edge Agent with a credential should provide proof to Cloud Agent Given Cloud Agent is connected to Edge Agent - And Edge Agent has 1 anonymous credentials issued by Cloud Agent + And Edge Agent has '1' anonymous credentials issued by Cloud Agent When Cloud Agent asks for presentation of AnonCred proof And Edge Agent sends the present-proof Then Cloud Agent should see the present-proof is verified diff --git a/integration-tests/e2e-tests/features/provide_proof.feature b/integration-tests/e2e-tests/features/provide_proof.feature index 29079126b..675233b85 100644 --- a/integration-tests/e2e-tests/features/provide_proof.feature +++ b/integration-tests/e2e-tests/features/provide_proof.feature @@ -4,7 +4,7 @@ Feature: Provide proof Scenario: Edge Agent with a credential should provide proof to Cloud Agent Given Cloud Agent is connected to Edge Agent - And Edge Agent has 1 credentials issued by Cloud Agent + And Edge Agent has '1' jwt credentials issued by Cloud Agent When Cloud Agent asks for present-proof And Edge Agent sends the present-proof Then Cloud Agent should see the present-proof is verified diff --git a/integration-tests/e2e-tests/features/receive_anoncred_credential.feature b/integration-tests/e2e-tests/features/receive_anoncred_credential.feature index 91b936c61..d3b70ce84 100644 --- a/integration-tests/e2e-tests/features/receive_anoncred_credential.feature +++ b/integration-tests/e2e-tests/features/receive_anoncred_credential.feature @@ -1,13 +1,12 @@ -@anoncred -@credential +@anoncred @credential Feature: Receive Anoncred Credential The Edge Agent should be able to receive an anonymous credential from Cloud Agent Scenario: Receive one anonymous credential Given Cloud Agent is connected to Edge Agent - When Cloud Agent offers an anonymous credential - Then Edge Agent should receive the credential - When Edge Agent accepts the credential - And Cloud Agent should see the credential was accepted - Then Edge Agent wait to receive 1 issued credentials - And Edge Agent process 1 issued credentials + When Cloud Agent offers '1' anonymous credential + Then Edge Agent should receive the credentials offer from Cloud Agent + When Edge Agent accepts the credentials offer from Cloud Agent + And Cloud Agent should see all credentials were accepted + Then Edge Agent wait to receive issued credentials from Cloud Agent + And Edge Agent process issued credentials from Cloud Agent diff --git a/integration-tests/e2e-tests/features/receive_jwt_credential.feature b/integration-tests/e2e-tests/features/receive_jwt_credential.feature index 0ad64bb36..174985ac4 100644 --- a/integration-tests/e2e-tests/features/receive_jwt_credential.feature +++ b/integration-tests/e2e-tests/features/receive_jwt_credential.feature @@ -1,27 +1,26 @@ -@jwt -@credential +@jwt @credential Feature: Receive JWT Credential The Edge Agent should be able to receive a verifiable credential from Cloud Agent Scenario: Receive one verifiable credential Given Cloud Agent is connected to Edge Agent - When Cloud Agent offers a credential - Then Edge Agent should receive the credential - When Edge Agent accepts the credential - And Cloud Agent should see the credential was accepted - Then Edge Agent wait to receive 1 issued credentials - And Edge Agent process 1 issued credentials + When Cloud Agent offers '1' jwt credentials + Then Edge Agent should receive the credentials offer from Cloud Agent + When Edge Agent accepts the credentials offer from Cloud Agent + And Cloud Agent should see all credentials were accepted + Then Edge Agent wait to receive issued credentials from Cloud Agent + And Edge Agent process issued credentials from Cloud Agent Scenario: Receive multiple verifiable credentials sequentially Given Cloud Agent is connected to Edge Agent - When Edge Agent accepts 3 credential offer sequentially from Cloud Agent + When Edge Agent accepts 3 jwt credential offer sequentially from Cloud Agent Then Cloud Agent should see all credentials were accepted - And Edge Agent wait to receive 3 issued credentials - And Edge Agent process 3 issued credentials + And Edge Agent wait to receive issued credentials from Cloud Agent + And Edge Agent process issued credentials from Cloud Agent Scenario: Receive multiple verifiable credentials at once Given Cloud Agent is connected to Edge Agent - When Edge Agent accepts 3 credentials offer at once from Cloud Agent + When Edge Agent accepts 3 jwt credentials offer at once from Cloud Agent Then Cloud Agent should see all credentials were accepted - And Edge Agent wait to receive 3 issued credentials - And Edge Agent process 3 issued credentials + And Edge Agent wait to receive issued credentials from Cloud Agent + And Edge Agent process issued credentials from Cloud Agent diff --git a/integration-tests/e2e-tests/features/revoke_jwt_credential.feature b/integration-tests/e2e-tests/features/revoke_jwt_credential.feature new file mode 100644 index 000000000..289fc40dc --- /dev/null +++ b/integration-tests/e2e-tests/features/revoke_jwt_credential.feature @@ -0,0 +1,11 @@ +@jwt @revocation +Feature: Revoke JWT Credential + Edge Agent should be notified when Cloud Agent revokes a credential + + Scenario: Revoke one verifiable credential + Given Cloud Agent is connected to Edge Agent + And Edge Agent has '1' jwt credentials issued by Cloud Agent + When Cloud Agent revokes '1' credentials + Then Edge Agent waits to receive the revocation notifications from Cloud Agent + And Edge Agent should see the credentials were revoked by Cloud Agent + \ No newline at end of file diff --git a/integration-tests/e2e-tests/package.json b/integration-tests/e2e-tests/package.json index 1eece94cf..3c1019ce7 100644 --- a/integration-tests/e2e-tests/package.json +++ b/integration-tests/e2e-tests/package.json @@ -17,7 +17,7 @@ }, "homepage": "https://github.com/input-output-hk/atala-prism-wallet-sdk-ts-e2e", "dependencies": { - "@atala/prism-wallet-sdk": "^5.0.0", + "@atala/prism-wallet-sdk": "../../", "@cucumber/cucumber": "^10.3.1", "@cucumber/pretty-formatter": "^1.0.0", "@hyperledger-labs/open-enterprise-agent-ts-client": "^1.31.0", diff --git a/integration-tests/e2e-tests/src/Utils.ts b/integration-tests/e2e-tests/src/Utils.ts index 2e1772380..a778ddc2a 100644 --- a/integration-tests/e2e-tests/src/Utils.ts +++ b/integration-tests/e2e-tests/src/Utils.ts @@ -2,6 +2,11 @@ import { appendFile, writeFileSync } from "fs" import crypto from "crypto" export class Utils { + static async asyncFilter(arr: T[], predicate: (value: T, index: number, array: T[]) => Promise) { + const results = await Promise.all(arr.map(predicate)) + return arr.filter((_v, index) => results[index]) + } + static prepareNotes() { writeFileSync("notes", "### End-to-end notes:\n\n") } diff --git a/integration-tests/e2e-tests/src/abilities/WalletSdk.ts b/integration-tests/e2e-tests/src/abilities/WalletSdk.ts index 71ba3c883..73d46bdeb 100644 --- a/integration-tests/e2e-tests/src/abilities/WalletSdk.ts +++ b/integration-tests/e2e-tests/src/abilities/WalletSdk.ts @@ -4,24 +4,26 @@ import { Message } from "@atala/prism-wallet-sdk/build/typings/domain" import axios from "axios" import { CloudAgentConfiguration } from "../configuration/CloudAgentConfiguration" import { Utils } from "../Utils" -import { InMemoryStore } from "../configuration/InMemoryStore" +import InMemoryStore from "../configuration/inmemory" const { Agent, Apollo, Domain, ListenerKey, } = SDK export class WalletSdk extends Ability implements Initialisable, Discardable { sdk!: SDK.Agent + store: SDK.Store messages: MessageQueue = new MessageQueue() static async withANewInstance(): Promise { - const instance: SDK.Agent = await Utils.retry(2, async () => { + const {sdk, store} = await Utils.retry(2, async () => { return await WalletSdkBuilder.createInstance() }) - return new WalletSdk(instance) + return new WalletSdk(sdk, store) } - constructor(sdk: SDK.Agent) { + constructor(sdk: SDK.Agent, store: SDK.Store) { super() this.sdk = sdk + this.store = store } static credentialOfferStackSize(): QuestionAdapter { @@ -42,21 +44,30 @@ export class WalletSdk extends Ability implements Initialisable, Discardable { }) } + static revocationStackSize(): QuestionAdapter { + return Question.about("revocation messages stack", actor => { + return WalletSdk.as(actor).messages.revocationStack.length + }) + } + static execute(callback: (sdk: SDK.Agent, messages: { credentialOfferStack: Message[]; issuedCredentialStack: Message[]; proofRequestStack: Message[]; + revocationStack: Message[], }) => Promise): Interaction { return Interaction.where("#actor uses wallet sdk", async actor => { await callback(WalletSdk.as(actor).sdk, { credentialOfferStack: WalletSdk.as(actor).messages.credentialOfferStack, issuedCredentialStack: WalletSdk.as(actor).messages.issuedCredentialStack, - proofRequestStack: WalletSdk.as(actor).messages.proofRequestStack + proofRequestStack: WalletSdk.as(actor).messages.proofRequestStack, + revocationStack: WalletSdk.as(actor).messages.revocationStack, }) }) } async discard(): Promise { + await this.store.clear() await this.sdk.stop() } @@ -87,11 +98,19 @@ class WalletSdkBuilder { static async createInstance() { const apollo = new Apollo() - const store = new InMemoryStore() + const store = new SDK.Store({ + name: [...Array(30)].map(() => Math.random().toString(36)[2]).join(""), + storage: InMemoryStore, + password: "random12434", + ignoreDuplicate: true + }) const pluto = new SDK.Pluto(store, apollo) const mediatorDID = Domain.DID.fromString(await WalletSdkBuilder.getMediatorDidThroughOob()) - return Agent.initialize({ apollo, pluto, mediatorDID }) + return { + sdk: Agent.initialize({ apollo, pluto, mediatorDID }), + store + } } } @@ -105,8 +124,10 @@ class MessageQueue { credentialOfferStack: Message[] = [] proofRequestStack: Message[] = [] issuedCredentialStack: Message[] = [] + revocationStack: Message[] = [] receivedMessages: string[] = [] + enqueue(message: Message) { this.queue.push(message) @@ -134,6 +155,7 @@ class MessageQueue { this.processingId = setInterval(() => { if (!this.isEmpty()) { const message: Message = this.dequeue() + // checks if sdk already received message if (this.receivedMessages.includes(message.id)) { return @@ -147,6 +169,8 @@ class MessageQueue { this.proofRequestStack.push(message) } else if (message.piuri.includes("/issue-credential")) { this.issuedCredentialStack.push(message) + } else if (message.piuri.includes("/revoke")) { + this.revocationStack.push(message) } } else { clearInterval(this.processingId!) diff --git a/integration-tests/e2e-tests/src/configuration/InMemoryStore.ts b/integration-tests/e2e-tests/src/configuration/InMemoryStore.ts deleted file mode 100644 index 187f310fa..000000000 --- a/integration-tests/e2e-tests/src/configuration/InMemoryStore.ts +++ /dev/null @@ -1,63 +0,0 @@ -import SDK from "@atala/prism-wallet-sdk" - -import { MangoQuery } from "rxdb" - -/** - * WARNING: Do not use this Pluto Store implementation, its for test purposes only. - * Persistence is inMemory and totally unprotected. - * Functionality isn't 100% covered - only handling what is necessary - */ -export class InMemoryStore implements SDK.Pluto.Store { - - private store = new Map() - - async query(table: string, query?: MangoQuery): Promise { - const items = this.get(table) - const selector = { ...query?.selector ?? {} } - - const filtered = items.filter(item => { - if (Object.keys(selector).length === 0) return true - - const { $or, $and, ...props } = selector - const matchProps = this.match(props, item) - const matchOr = ($or ?? []).reduce((acc, x) => acc || this.match(x, item), false) - const matchAnd = $and?.length > 0 ? ($and ?? []).reduce((acc, x) => acc && this.match(x, item), true) : false - return matchOr || matchAnd || matchProps - }) - - return filtered - } - - async insert(table: string, model: any): Promise { - const items = this.get(table) - items.push(model) - } - - private get(key: string) { - const current = this.store.get(key) - - if (!current) { - this.store.set(key, []) - } - - return this.store.get(key)! - } - - private match(query: Record, item: any) { - const keys = Object.keys(query) - if (keys.length <= 0) { - return false - } - const match = keys.every(key => item[key] == query[key]) - return match - } - - - update(table: string, model: T): Promise { - throw new Error("Method not implemented.") - } - - delete(table: string, uuid: string): Promise { - throw new Error("Method not implemented.") - } -} diff --git a/integration-tests/e2e-tests/src/configuration/inmemory/factory.ts b/integration-tests/e2e-tests/src/configuration/inmemory/factory.ts new file mode 100644 index 000000000..060c5e24b --- /dev/null +++ b/integration-tests/e2e-tests/src/configuration/inmemory/factory.ts @@ -0,0 +1,16 @@ +import { randomUUID } from "crypto"; +import SDK from "@atala/prism-wallet-sdk"; +import InMemoryStore from "./index"; + + +export const mockPluto = (args?: { apollo: SDK.Apollo; }) => { + const apollo = args?.apollo ?? new SDK.Apollo(); + + const store = new SDK.Store({ + name: 'test' + randomUUID(), + storage: InMemoryStore, + password: Buffer.from("demoapp").toString("hex") + }); + + return new SDK.Pluto(store, apollo); +}; diff --git a/integration-tests/e2e-tests/src/configuration/inmemory/index.ts b/integration-tests/e2e-tests/src/configuration/inmemory/index.ts new file mode 100644 index 000000000..d5ecd20a8 --- /dev/null +++ b/integration-tests/e2e-tests/src/configuration/inmemory/index.ts @@ -0,0 +1,44 @@ +import { type RxStorage, RxStorageDefaultStatics, type RxStorageInstance, type RxStorageInstanceCreationParams, newRxError } from 'rxdb' +import { type InMemorySettings, type InMemoryStorageInternals, type RxStorageInMemoryType } from './types' +import { RxStorageIntanceInMemory } from './instance' +import { InMemoryInternal } from './internal' + +const internalInstance = new Map>() + +function getRxStorageMemory(settings: InMemorySettings = {}): RxStorageInMemoryType { + const inMemoryInstance: RxStorageInMemoryType = { + name: 'in-memory', + statics: RxStorageDefaultStatics, + async createStorageInstance(params: RxStorageInstanceCreationParams): Promise, InMemorySettings, any>> { + if (params.schema.keyCompression) { + throw newRxError('UT5', { args: { databaseName: params.databaseName, collectionName: params.collectionName } }) + } + const existingInstance = internalInstance.get(params.databaseName) + if (!existingInstance) { + internalInstance.set(params.databaseName, new InMemoryInternal(0)) + } else { + existingInstance.refCount++ + internalInstance.set(params.databaseName, existingInstance) + } + return new RxStorageIntanceInMemory( + this, + params.databaseName, + params.collectionName, + params.schema, + internalInstance.get(params.databaseName)!, + settings + ) + } + } + return inMemoryInstance +} + +/** + * InMemory storage + * @description Use this as storage in our RXDB database. For now there is no initialisation settings, so you can use it out of the box. + */ +const storage: RxStorage = getRxStorageMemory() + +export default storage + + diff --git a/integration-tests/e2e-tests/src/configuration/inmemory/instance.ts b/integration-tests/e2e-tests/src/configuration/inmemory/instance.ts new file mode 100644 index 000000000..ab72f638f --- /dev/null +++ b/integration-tests/e2e-tests/src/configuration/inmemory/instance.ts @@ -0,0 +1,283 @@ +import type { + RxStorageInstance, + RxStorageDefaultCheckpoint, + StringKeys, + RxDocumentData, + EventBulk, + RxStorageChangeEvent, + RxJsonSchema, + BulkWriteRow, + RxStorageBulkWriteResponse, + RxDocumentDataById, + RxStorageQueryResult, + RxStorageCountResult, + RxConflictResultionTask, +} from 'rxdb'; + +import type { + QueryMatcher +} from 'rxdb/dist/types/types'; + +import type { + Observable +} from 'rxjs'; + +import type { + InMemoryStorageInternals, + InMemorySettings, + RxStorageInMemoryType, + InMemoryPreparedQuery +} from './types'; + + +import { + categorizeBulkWriteRows, + ensureNotFalsy, + now, + getPrimaryFieldOfPrimaryKey, + getQueryMatcher, + getSortComparator +} from 'rxdb'; + +import { + Subject +} from 'rxjs' + +function fixTxPipe(str: string): string { + const split = str.split('.') + if (split.length > 1) { + return split.map(part => fixTxPipe(part)).join('.') + } + + return str +} + +export class RxStorageIntanceInMemory implements RxStorageInstance< + RxDocType, + InMemoryStorageInternals, + InMemorySettings, + RxStorageDefaultCheckpoint> { + public readonly primaryPath: StringKeys> + public conflictResultionTasks$ = new Subject>() + public changes$ = new Subject>, RxStorageDefaultCheckpoint>>() + public closed: boolean = false + + constructor( + public readonly storage: RxStorageInMemoryType, + public readonly databaseName: string, + public readonly collectionName: string, + public readonly schema: Readonly>>, + public readonly internals: InMemoryStorageInternals, + public readonly options: Readonly + ) { + this.primaryPath = getPrimaryFieldOfPrimaryKey(this.schema.primaryKey) + } + + async bulkWrite( + documentWrites: Array>, + context: string): Promise> { + const primaryPath = this.primaryPath + const ret: RxStorageBulkWriteResponse = { + success: {}, + error: {} + } + + const documents = this.internals.documents + const fixed = documentWrites.reduce>>((fixedDocs, currentWriteDoc) => { + const currentId = currentWriteDoc.document[this.primaryPath] as any + const previousDocument = currentWriteDoc.previous ?? this.internals.documents.get(currentId) + if (context === 'data-migrator-delete') { + if (previousDocument) { + currentWriteDoc.document = { + ...previousDocument, + _deleted: true + } + currentWriteDoc.previous = { + ...previousDocument, + _deleted: false + } + fixedDocs.push(currentWriteDoc); + } + } else { + if (previousDocument && previousDocument._rev !== currentWriteDoc.document._rev) { + currentWriteDoc.previous = previousDocument + } else { + currentWriteDoc.previous = undefined + } + fixedDocs.push(currentWriteDoc) + } + return fixedDocs + }, []) + + const categorized = categorizeBulkWriteRows( + this, + primaryPath as any, + documents as any, + fixed, + context + ) + ret.error = categorized.errors + + /** + * Do inserts/updates + */ + const bulkInsertDocs = categorized.bulkInsertDocs + for (let i = 0; i < bulkInsertDocs.length; ++i) { + const writeRow = bulkInsertDocs[i]! + const docId = writeRow.document[primaryPath] + await this.internals.bulkPut([writeRow.document], this.collectionName, this.schema) + ret.success[docId as any] = writeRow.document + } + + const bulkUpdateDocs = categorized.bulkUpdateDocs + for (let i = 0; i < bulkUpdateDocs.length; ++i) { + const writeRow = bulkUpdateDocs[i]! + const docId = writeRow.document[primaryPath] + await this.internals.bulkPut([writeRow.document], this.collectionName, this.schema) + ret.success[docId as any] = writeRow.document + } + + if (categorized.eventBulk.events.length > 0) { + const lastState = ensureNotFalsy(categorized.newestRow).document + categorized.eventBulk.checkpoint = { + id: lastState[primaryPath], + lwt: lastState._meta.lwt + } + const endTime = now() + categorized.eventBulk.events.forEach(event => { + (event as any).endTime = endTime + }) + this.changes$.next(categorized.eventBulk) + } + + return await Promise.resolve(ret) + } + + async findDocumentsById(ids: string[], withDeleted: boolean): Promise> { + return this.internals.bulkGet(ids, withDeleted) + } + + async query(preparedQuery: InMemoryPreparedQuery): Promise> { + const { queryPlan, query } = preparedQuery + const selector = query.selector + const selectorKeys = Object.keys(selector) + const skip = query.skip ? query.skip : 0 + const limit = query.limit ? query.limit : Infinity + const skipPlusLimit = skip + limit + const queryMatcher: QueryMatcher> = getQueryMatcher( + this.schema, + query + ) + + const queryPlanFields: string[] = queryPlan.index + const indexes: string[] = [] + if (queryPlanFields.length === 1) { + indexes.push(fixTxPipe(queryPlanFields[0]!)) + } else { + indexes.push(...queryPlanFields.map(field => fixTxPipe(field))) + } + + const shouldAddCompoundIndexes = this.schema.indexes?.find((index) => { + if (typeof index === 'string') { + return indexes.find((index2) => index2 === index) + } else { + return index.find((subIndex) => { + return subIndex === index.find((indexValue) => indexValue === subIndex) + }) + } + }) + + if (shouldAddCompoundIndexes) { + indexes.splice(0, indexes.length) + indexes.push(this.collectionName) + if (typeof shouldAddCompoundIndexes === 'string') { + indexes.push(shouldAddCompoundIndexes) + } else { + indexes.push(...shouldAddCompoundIndexes) + } + } else { + indexes.unshift(this.collectionName) + } + + const indexName: string = `[${indexes.join('+')}]` + const documentIds = this.internals.index.get(indexName) + + if (!documentIds) { + return { documents: [] } + } + + let documents = documentIds.reduce>>((allDocuments, id) => { + const document = this.internals.data.get(id) + if (document) { + if (selectorKeys.length <= 0) { + return [...allDocuments, document] + } + const matches = queryMatcher(document) + if (matches) { + return [...allDocuments, document] + } + } + return allDocuments + }, []) + + const sortComparator = getSortComparator(this.schema, preparedQuery.query) + documents = documents.sort(sortComparator) + documents = documents.slice(skip, skipPlusLimit) + return { + documents + } + } + + async count(preparedQuery: any): Promise { + const result = await this.query(preparedQuery) + return { + count: result.documents.length, + mode: 'fast' + } + } + + /* istanbul ignore next */ + async getAttachmentData(): Promise { + throw new Error('Method not implemented.') + } + + /* istanbul ignore next */ + async getChangedDocumentsSince(): Promise<{ documents: Array>, checkpoint: RxStorageDefaultCheckpoint }> { + throw new Error('Method not implemented.') + } + + /* istanbul ignore next */ + changeStream(): Observable, RxStorageDefaultCheckpoint>> { + return this.changes$.asObservable() + } + + async cleanup(): Promise { + this.internals.clear() + + return true + } + + /* istanbul ignore next */ + async close(): Promise { + if (this.closed) { + await Promise.reject(new Error('already closed')); return + } + this.closed = true + + this.internals.refCount = this.internals.refCount - 1 + } + + /* istanbul ignore next */ + async remove(): Promise { + await Promise.resolve() + } + + conflictResultionTasks(): Observable> { + return this.conflictResultionTasks$.asObservable() + } + + /* istanbul ignore next */ + async resolveConflictResultionTask(): Promise { + await Promise.resolve() + } +} diff --git a/integration-tests/e2e-tests/src/configuration/inmemory/internal.ts b/integration-tests/e2e-tests/src/configuration/inmemory/internal.ts new file mode 100644 index 000000000..22e014c04 --- /dev/null +++ b/integration-tests/e2e-tests/src/configuration/inmemory/internal.ts @@ -0,0 +1,118 @@ +import type { + RxDocumentData, + RxDocumentDataById, + RxJsonSchema +} from 'rxdb'; + +import type { + InMemoryDataIndex, + InMemoryDataStructure, + InMemoryStorageInternals, + IndexType +} from './types'; + +function safeIndexList(schema: Readonly>>) { + const primaryKeyKey = typeof schema.primaryKey === 'string' ? schema.primaryKey : schema.primaryKey.key + const allIndexes: string[][] = [] + for (const requiredIndexes of (schema.indexes ?? [])) { + const currentIndexes: string[] = [] + if (typeof requiredIndexes === 'string') { + currentIndexes.push(requiredIndexes) + } else { + currentIndexes.push(...requiredIndexes) + } + if (!currentIndexes.includes(primaryKeyKey)) { + currentIndexes.unshift(primaryKeyKey) + } + allIndexes.push(currentIndexes) + } + return allIndexes +} + +function getPrivateKeyValue(document: RxDocumentData, schema: Readonly>>) { + const primaryKeyKey = typeof schema.primaryKey === 'string' ? schema.primaryKey : schema.primaryKey.key + if (!primaryKeyKey) { + throw new Error('Data must have a primaryKey defined of type string or number') + } + const id = document[primaryKeyKey] as string + return id +} + +function initialiseData(): InMemoryDataStructure { + return new Map() +} + +function initialiseIndex(): InMemoryDataIndex { + return new Map() +} + +export class InMemoryInternal implements InMemoryStorageInternals { + public removed = false + public data: InMemoryDataStructure = initialiseData() + public index: InMemoryDataIndex = initialiseIndex() + + constructor(public refCount: number) { } + + get documents() { + return this.data + } + + public addIndex(indexName: string, docId: IndexType) { + if (this.index.has(indexName)) { + const values = this.index.get(indexName) ?? [] + const newIndexes = Array.from(new Set([...values, docId])) + this.index.set(indexName, newIndexes) + } else { + this.index.set(indexName, [docId]) + } + } + + public removeFromIndex(indexName: string, id: string) { + if (this.index.has(indexName)) { + const values = this.index.get(indexName) ?? [] + this.index.set(indexName, values.filter((vId) => vId !== id)) + } + } + + clear() { + this.data.clear() + this.index.clear() + } + + async bulkPut(items: Array>, collectionName: string, schema: Readonly>>) { + const primaryKeyKey = typeof schema.primaryKey === 'string' ? schema.primaryKey : schema.primaryKey.key + const saferIndexList = safeIndexList(schema) + + for (const item of items) { + const shouldDelete = item._deleted + const id = getPrivateKeyValue(item, schema) + if (shouldDelete) { + for (const requiredIndexes of saferIndexList) { + const requiredIndex = `[${collectionName}+${requiredIndexes.join('+')}]` + await this.removeFromIndex(requiredIndex, id) + } + await this.removeFromIndex(`[${collectionName}+${primaryKeyKey}]`, id) + await this.removeFromIndex('[all]', id) + await this.data.delete(id) + } else { + for (const requiredIndexes of saferIndexList) { + const requiredIndex = `[${collectionName}+${requiredIndexes.join('+')}]` + await this.addIndex(requiredIndex, id) + } + await this.addIndex(`[${collectionName}+${primaryKeyKey}]`, id) + await this.addIndex('[all]', id) + await this.data.set(id, item) + } + } + } + + bulkGet(docIds: string[]): RxDocumentDataById { + return docIds.reduce>((alldocs, current) => { + const data = this.data.get(current) + if (data) { + alldocs[current] = data + } + return alldocs + }, {}) + } +} diff --git a/integration-tests/e2e-tests/src/configuration/inmemory/types.ts b/integration-tests/e2e-tests/src/configuration/inmemory/types.ts new file mode 100644 index 000000000..dac648a29 --- /dev/null +++ b/integration-tests/e2e-tests/src/configuration/inmemory/types.ts @@ -0,0 +1,49 @@ +import type { + DefaultPreparedQuery, + RxDocumentData, + RxDocumentDataById, + RxJsonSchema, + RxStorage +} from 'rxdb' + +/** + * Index of a table can be a string or a number + */ +export type IndexType = string | number +/** + * InMemory internal data structure is a Map with an index + * and RxDocumentData from RXDB + */ +export type InMemoryDataStructure = Map> +/** + * Data type for index keystorage + * I used this to get faster searches based on what RXDB indexes we were + * informed + */ +export type InMemoryDataIndex = Map +/** + * Query type for InMemory + */ +export type InMemoryPreparedQuery = DefaultPreparedQuery +/** + * Main storage interface for InMemoryStorage + */ +export interface InMemoryStorageInternals { + data: InMemoryDataStructure + index: InMemoryDataIndex + documents: InMemoryDataStructure + removed: boolean + refCount: number + addIndex: (indexName: string, docId: IndexType) => any + removeFromIndex: (indexName: string, id: string) => void + bulkPut: ( + items: any, + collectionName: string, + schema: Readonly>>) => any + bulkGet: (docIds: string[], withDeleted: boolean) => RxDocumentDataById + clear: () => void +} + +export type RxStorageInMemoryType = RxStorage + +export interface InMemorySettings { } diff --git a/integration-tests/e2e-tests/src/steps/CloudAgentSteps.ts b/integration-tests/e2e-tests/src/steps/CloudAgentSteps.ts index dec90694f..e84c41c9a 100644 --- a/integration-tests/e2e-tests/src/steps/CloudAgentSteps.ts +++ b/integration-tests/e2e-tests/src/steps/CloudAgentSteps.ts @@ -2,6 +2,8 @@ import {Given, Then, When} from "@cucumber/cucumber" import {Actor, Notepad} from "@serenity-js/core" import {CloudAgentWorkflow} from "../workflow/CloudAgentWorkflow" import {EdgeAgentWorkflow} from "../workflow/EdgeAgentWorkflow" +import { Utils } from "../Utils" +import { WalletSdk } from "../abilities/WalletSdk" Given("{actor} has a connection invitation with '{}', '{}' and '{}' parameters", async function (cloudAgent: Actor, rawLabel: string, rawGoalCode: string, rawGoal: string) { @@ -22,12 +24,26 @@ Given("{actor} shares invitation to {actor}", async function (cloudAgent: Actor, await CloudAgentWorkflow.shareInvitation(cloudAgent, edgeAgent) }) -When("{actor} offers a credential", async function (cloudAgent: Actor) { - await CloudAgentWorkflow.offerCredential(cloudAgent) +When("{actor} offers '{int}' jwt credentials", async function (cloudAgent: Actor, numberOfCredentials: number) { + const recordIdList = [] + await Utils.repeat(numberOfCredentials, async () => { + await CloudAgentWorkflow.offerCredential(cloudAgent) + recordIdList.push(await cloudAgent.answer(Notepad.notes().get("recordId"))) + }) + await cloudAgent.attemptsTo( + Notepad.notes().set("recordIdList", recordIdList) + ) }) -When("{actor} offers an anonymous credential", async function(cloudAgent: Actor) { - await CloudAgentWorkflow.offerAnonymousCredential(cloudAgent) +When("{actor} offers '{int}' anonymous credential", async function(cloudAgent: Actor, numberOfAnoncreds: number) { + const recordIdList = [] + await Utils.repeat(numberOfAnoncreds, async () => { + await CloudAgentWorkflow.offerAnonymousCredential(cloudAgent) + recordIdList.push(await cloudAgent.answer(Notepad.notes().get("recordId"))) + }) + await cloudAgent.attemptsTo( + Notepad.notes().set("recordIdList", recordIdList) + ) }) When("{actor} asks for present-proof", async function (cloudAgent: Actor) { @@ -38,13 +54,12 @@ When("{actor} asks for presentation of AnonCred proof", async function (cloudAge await CloudAgentWorkflow.askForPresentProofAnonCreds(cloudAgent) }) -Then("{actor} should have the connection status updated to '{}'", async (cloudAgent: Actor, expectedStatus: string) => { - await CloudAgentWorkflow.waitForConnectionState(cloudAgent, expectedStatus) +When("{actor} revokes '{int}' credentials", async function (cloudAgent: Actor, numberOfRevokedCredentials: number) { + await CloudAgentWorkflow.revokeCredential(cloudAgent, numberOfRevokedCredentials) }) -Then("{actor} should see the credential was accepted", async (cloudAgent: Actor) => { - const recordId = await cloudAgent.answer(Notepad.notes().get("recordId")) - await CloudAgentWorkflow.verifyCredentialState(cloudAgent, recordId, "CredentialSent") +Then("{actor} should have the connection status updated to '{}'", async (cloudAgent: Actor, expectedStatus: string) => { + await CloudAgentWorkflow.waitForConnectionState(cloudAgent, expectedStatus) }) Then("{actor} should see the present-proof is verified", async (cloudAgent: Actor) => { diff --git a/integration-tests/e2e-tests/src/steps/EdgeAgentSteps.ts b/integration-tests/e2e-tests/src/steps/EdgeAgentSteps.ts index fc00a1ad3..e117d025b 100644 --- a/integration-tests/e2e-tests/src/steps/EdgeAgentSteps.ts +++ b/integration-tests/e2e-tests/src/steps/EdgeAgentSteps.ts @@ -4,38 +4,44 @@ import { EdgeAgentWorkflow } from "../workflow/EdgeAgentWorkflow" import { CloudAgentWorkflow } from "../workflow/CloudAgentWorkflow" import { Utils } from "../Utils" -Given("{actor} has {int} credentials issued by {actor}", +Given("{actor} has '{int}' jwt credentials issued by {actor}", async function (edgeAgent: Actor, numberOfIssuedCredentials: number, cloudAgent: Actor) { + const recordIdList = [] await Utils.repeat(numberOfIssuedCredentials, async () => { await CloudAgentWorkflow.offerCredential(cloudAgent) - await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent) + await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent, 1) await EdgeAgentWorkflow.acceptCredential(edgeAgent) const recordId = await cloudAgent.answer(Notepad.notes().get("recordId")) + recordIdList.push(recordId) await CloudAgentWorkflow.verifyCredentialState(cloudAgent, recordId, "CredentialSent") await EdgeAgentWorkflow.waitToReceiveCredentialIssuance(edgeAgent, 1) - await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, 1) + await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, recordId) }) + await cloudAgent.attemptsTo(Notepad.notes().set("recordIdList", recordIdList)) }) -Given("{actor} has {int} anonymous credentials issued by {actor}", +Given("{actor} has '{int}' anonymous credentials issued by {actor}", async function (edgeAgent: Actor, numberOfIssuedCredentials: number, cloudAgent: Actor) { + const recordIdList = [] await Utils.repeat(numberOfIssuedCredentials, async () => { await CloudAgentWorkflow.offerAnonymousCredential(cloudAgent) - await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent) + await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent, 1) await EdgeAgentWorkflow.acceptCredential(edgeAgent) const recordId = await cloudAgent.answer(Notepad.notes().get("recordId")) + recordIdList.push(recordId) await CloudAgentWorkflow.verifyCredentialState(cloudAgent, recordId, "CredentialSent") await EdgeAgentWorkflow.waitToReceiveCredentialIssuance(edgeAgent, 1) - await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, 1) + await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, recordId) }) + await cloudAgent.attemptsTo(Notepad.notes().set("recordIdList", recordIdList)) }) -When("{actor} accepts {int} credential offer sequentially from {actor}", +When("{actor} accepts {int} jwt credential offer sequentially from {actor}", async function (edgeAgent: Actor, numberOfCredentialOffers: number, cloudAgent: Actor) { const recordIdList: string[] = [] await Utils.repeat(numberOfCredentialOffers, async () => { await CloudAgentWorkflow.offerCredential(cloudAgent) - await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent) + await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent, 1) await EdgeAgentWorkflow.acceptCredential(edgeAgent) const recordId = await cloudAgent.answer(Notepad.notes().get("recordId")) await CloudAgentWorkflow.verifyCredentialState(cloudAgent, recordId, "CredentialSent") @@ -44,7 +50,7 @@ When("{actor} accepts {int} credential offer sequentially from {actor}", await cloudAgent.attemptsTo(Notepad.notes().set("recordIdList", recordIdList)) }) -When("{actor} accepts {int} credentials offer at once from {actor}", +When("{actor} accepts {int} jwt credentials offer at once from {actor}", async function (edgeAgent: Actor, numberOfCredentials: number, cloudAgent: Actor) { const recordIdList: string[] = [] await Utils.repeat(numberOfCredentials, async () => { @@ -66,9 +72,12 @@ When("{actor} connects through the invite", await EdgeAgentWorkflow.connect(edgeAgent) }) -When("{actor} accepts the credential", - async function (edgeAgent: Actor) { - await EdgeAgentWorkflow.acceptCredential(edgeAgent) +When("{actor} accepts the credentials offer from {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList")) + Utils.repeat(recordIdList.length, async () => { + await EdgeAgentWorkflow.acceptCredential(edgeAgent) + }) }) When("{actor} sends the present-proof", @@ -77,17 +86,34 @@ When("{actor} sends the present-proof", await EdgeAgentWorkflow.presentProof(edgeAgent) }) -Then("{actor} should receive the credential", - async function (edgeAgent: Actor) { - await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent) +Then("{actor} should receive the credentials offer from {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList")) + await EdgeAgentWorkflow.waitForCredentialOffer(edgeAgent, recordIdList.length) + }) + +Then("{actor} waits to receive the revocation notifications from {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const revokedRecordIdList = await cloudAgent.answer(Notepad.notes().get("revokedRecordIdList")) + await EdgeAgentWorkflow.waitForCredentialRevocationMessage(edgeAgent, revokedRecordIdList.length) + }) + +Then("{actor} should see the credentials were revoked by {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const revokedRecordIdList = await cloudAgent.answer(Notepad.notes().get("revokedRecordIdList")) + await EdgeAgentWorkflow.waitUntilCredentialIsRevoked(edgeAgent, revokedRecordIdList) }) -Then("{actor} process {int} issued credentials", - async function (edgeAgent: Actor, numberOfCredentials: number) { - await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, numberOfCredentials) +Then("{actor} process issued credentials from {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList")) + for (const recordId of recordIdList) { + await EdgeAgentWorkflow.processIssuedCredential(edgeAgent, recordId) + } }) -Then("{actor} wait to receive {int} issued credentials", - async function (edgeAgent: Actor, expectedNumberOfCredentials: number) { - await EdgeAgentWorkflow.waitToReceiveCredentialIssuance(edgeAgent, expectedNumberOfCredentials) +Then("{actor} wait to receive issued credentials from {actor}", + async function (edgeAgent: Actor, cloudAgent: Actor) { + const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList")) + await EdgeAgentWorkflow.waitToReceiveCredentialIssuance(edgeAgent, recordIdList.length) }) diff --git a/integration-tests/e2e-tests/src/workflow/CloudAgentWorkflow.ts b/integration-tests/e2e-tests/src/workflow/CloudAgentWorkflow.ts index 9959cc70d..23ba4da60 100644 --- a/integration-tests/e2e-tests/src/workflow/CloudAgentWorkflow.ts +++ b/integration-tests/e2e-tests/src/workflow/CloudAgentWorkflow.ts @@ -1,5 +1,5 @@ import { Actor, Duration, Notepad, Wait } from "@serenity-js/core" -import { LastResponse, PostRequest, Send } from "@serenity-js/rest" +import { LastResponse, PatchRequest, PostRequest, Send } from "@serenity-js/rest" import { Ensure, equals } from "@serenity-js/assertions" import { HttpStatusCode } from "axios" import { Expectations } from "../screenplay/Expectations" @@ -45,7 +45,7 @@ export class CloudAgentWorkflow { Notepad.notes().get("connectionId") ) await cloudAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( Questions.httpGet(`connections/${connectionId}`), Expectations.propertyValueToBe("state", state) ) @@ -54,7 +54,7 @@ export class CloudAgentWorkflow { static async verifyCredentialState(cloudAgent: Actor, recordId: string, state: string) { await cloudAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( Questions.httpGet(`issue-credentials/records/${recordId}`), Expectations.propertyValueToBe("protocolState", state) ) @@ -66,7 +66,7 @@ export class CloudAgentWorkflow { Notepad.notes().get("presentationId") ) await cloudAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( Questions.httpGet(`present-proof/presentations/${presentationId}`), Expectations.propertyValueToBe("status", state) ) @@ -86,11 +86,8 @@ export class CloudAgentWorkflow { ) await cloudAgent.attemptsTo( - Send.a( - PostRequest.to("issue-credentials/credential-offers").with(credential) - ) - ) - await cloudAgent.attemptsTo( + Send.a(PostRequest.to("issue-credentials/credential-offers").with(credential)), + Ensure.that(LastResponse.status(), equals(HttpStatusCode.Created)), Notepad.notes().set("recordId", LastResponse.body().recordId) ) } @@ -112,11 +109,8 @@ export class CloudAgentWorkflow { } await cloudAgent.attemptsTo( - Send.a( - PostRequest.to("issue-credentials/credential-offers").with(credential) - ) - ) - await cloudAgent.attemptsTo( + Send.a(PostRequest.to("issue-credentials/credential-offers").with(credential)), + Ensure.that(LastResponse.status(), equals(HttpStatusCode.Created)), Notepad.notes().set("recordId", LastResponse.body().recordId) ) } @@ -137,9 +131,8 @@ export class CloudAgentWorkflow { presentProofRequest.proofs = [proof] await cloudAgent.attemptsTo( - Send.a( - PostRequest.to("present-proof/presentations").with(presentProofRequest) - ), + Send.a(PostRequest.to("present-proof/presentations").with(presentProofRequest)), + Ensure.that(LastResponse.status(), equals(HttpStatusCode.Created)), Notepad.notes().set("presentationId", LastResponse.body().presentationId) ) } @@ -180,7 +173,26 @@ export class CloudAgentWorkflow { await cloudAgent.attemptsTo( Send.a(PostRequest.to("present-proof/presentations").with(presentationRequest)), + Ensure.that(LastResponse.status(), equals(HttpStatusCode.Created)), Notepad.notes().set("presentationId", LastResponse.body().presentationId) ) } + + static async revokeCredential(cloudAgent: Actor, numberOfRevokedCredentials: number) { + const revokedRecordIdList = [] + const recordIdList = await cloudAgent.answer(Notepad.notes().get("recordIdList")) + await Utils.repeat(numberOfRevokedCredentials, async () => { + const recordId = recordIdList.shift()! + await cloudAgent.attemptsTo( + Send.a(PatchRequest.to(`credential-status/revoke-credential/${recordId}`)), + Ensure.that(LastResponse.status(), equals(HttpStatusCode.Ok)) + ) + revokedRecordIdList.push(recordId) + }) + + await cloudAgent.attemptsTo( + Notepad.notes().set("recordIdList", recordIdList), + Notepad.notes().set("revokedRecordIdList", revokedRecordIdList) + ) + } } diff --git a/integration-tests/e2e-tests/src/workflow/EdgeAgentWorkflow.ts b/integration-tests/e2e-tests/src/workflow/EdgeAgentWorkflow.ts index 98a6c9d92..b07485395 100644 --- a/integration-tests/e2e-tests/src/workflow/EdgeAgentWorkflow.ts +++ b/integration-tests/e2e-tests/src/workflow/EdgeAgentWorkflow.ts @@ -1,25 +1,27 @@ import SDK from "@atala/prism-wallet-sdk" import { Actor, Duration, Notepad, Wait } from "@serenity-js/core" -import { equals } from "@serenity-js/assertions" +import { Ensure, equals } from "@serenity-js/assertions" import { WalletSdk } from "../abilities/WalletSdk" import { Utils } from "../Utils" const { IssueCredential, OfferCredential, RequestPresentation, } = SDK export class EdgeAgentWorkflow { + + static async connect(edgeAgent: Actor) { const url = await edgeAgent.answer(Notepad.notes().get("invitation")) await edgeAgent.attemptsTo( WalletSdk.execute(async (sdk) => { const oobInvitation = await sdk.parseOOBInvitation(new URL(url)) - await sdk.acceptDIDCommInvitation(oobInvitation) + await sdk.acceptInvitation(oobInvitation) }) ) } - static async waitForCredentialOffer(edgeAgent: Actor, numberOfCredentialOffer: number = 1) { + static async waitForCredentialOffer(edgeAgent: Actor, numberOfCredentialOffer: number) { await edgeAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( WalletSdk.credentialOfferStackSize(), equals(numberOfCredentialOffer) ) @@ -28,21 +30,20 @@ export class EdgeAgentWorkflow { static async waitToReceiveCredentialIssuance(edgeAgent: Actor, expectedNumberOfCredentials: number) { await edgeAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( WalletSdk.issuedCredentialStackSize(), equals(expectedNumberOfCredentials) ) ) } - static async processIssuedCredential(edgeAgent: Actor, numberOfCredentials: number) { + static async processIssuedCredential(edgeAgent: Actor, recordId: string) { await edgeAgent.attemptsTo( WalletSdk.execute(async (sdk, messages) => { - await Utils.repeat(numberOfCredentials, async () => { - const issuedCredential = messages.issuedCredentialStack.shift()! - const issueCredential = IssueCredential.fromMessage(issuedCredential) - await sdk.processIssuedCredentialMessage(issueCredential) - }) + const issuedCredential = messages.issuedCredentialStack.shift()! + const issueCredential = IssueCredential.fromMessage(issuedCredential) + const credential = await sdk.processIssuedCredentialMessage(issueCredential) + await edgeAgent.attemptsTo(Notepad.notes().set(recordId, credential.id)) }) ) } @@ -64,7 +65,7 @@ export class EdgeAgentWorkflow { static async waitForProofRequest(edgeAgent: Actor) { await edgeAgent.attemptsTo( - Wait.upTo(Duration.ofMinutes(2)).until( + Wait.upTo(Duration.ofSeconds(60)).until( WalletSdk.proofOfRequestStackSize(), equals(1), ), @@ -92,4 +93,31 @@ export class EdgeAgentWorkflow { ) ) } + + static async waitForCredentialRevocationMessage(edgeAgent: Actor, numberOfRevocation: number) { + await edgeAgent.attemptsTo( + Wait.upTo(Duration.ofSeconds(60)).until( + WalletSdk.revocationStackSize(), + equals(numberOfRevocation) + ) + ) + } + + static async waitUntilCredentialIsRevoked(edgeAgent: Actor, revokedRecordIdList: string[]) { + const revokedIdList = await Promise.all(revokedRecordIdList.map(async recordId => { + return await edgeAgent.answer(Notepad.notes().get(recordId)) + })) + await edgeAgent.attemptsTo( + WalletSdk.execute(async (sdk) => { + const credentials = await sdk.verifiableCredentials() + const revokedCredentials = await Utils.asyncFilter(credentials, async credential => { + // checks if it's revoked and part of the revoked ones + return credential.isRevoked() && revokedIdList.includes(credential.id) + }) + await edgeAgent.attemptsTo( + Ensure.that(revokedCredentials.length, equals(revokedRecordIdList.length)) + ) + }) + ) + } } diff --git a/integration-tests/e2e-tests/yarn.lock b/integration-tests/e2e-tests/yarn.lock index 40dd9bf8a..b58990f63 100644 --- a/integration-tests/e2e-tests/yarn.lock +++ b/integration-tests/e2e-tests/yarn.lock @@ -22,10 +22,8 @@ hash.js "1.1.7" stream-browserify "3.0.0" -"@atala/prism-wallet-sdk@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@atala/prism-wallet-sdk/-/prism-wallet-sdk-5.0.0.tgz#683e2180e343abb71b8d13b4a9008babc4d664d0" - integrity sha512-S7Jp/8ug6djr/CRGfBzUozo762bJsfSLIPTTRLVI730o+TFBesyUbyrfB2wb82xl9Hd31BsVcO71yjki77/PVw== +"@atala/prism-wallet-sdk@../../": + version "3.1.0" dependencies: "@atala/apollo" "^1.2.10" "@scure/bip32" "^1.3.0" @@ -46,7 +44,7 @@ google-protobuf "^3.21.2" hash.js "1.1.7" isows "^1.0.3" - jose "^4.12.2" + jose "^4.15.5" jsonwebtoken "^9.0.0" multiformats "^9.9.0" rxdb "^14.17.1" @@ -3106,7 +3104,7 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -jose@^4.12.2: +jose@^4.15.5: version "4.15.5" resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.5.tgz#6475d0f467ecd3c630a1b5dadd2735a7288df706" integrity sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg== @@ -4208,7 +4206,16 @@ string-argv@0.3.1: resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4260,7 +4267,14 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -4698,7 +4712,16 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0, wrap-ansi@^8.1.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0, wrap-ansi@^8.1.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==