diff --git a/README.md b/README.md index 623189bb5b..fd8744ae33 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs - 🏃 **Platform agnostic** - out of the box support for Node.JS and React Native - 🔒 **DIDComm and AIP** - Support for [DIDComm v1](https://hyperledger.github.io/aries-rfcs/latest/concepts/0005-didcomm/), and both v1 and v2 of the [Aries Interop Profile](https://github.com/hyperledger/aries-rfcs/blob/main/concepts/0302-aries-interop-profile/README.md). -- 🛂 **Extendable [DID](https://www.w3.org/TR/did-core/) resolver and registrar** - out of the box support for `did:web`, `did:key`, `did:jwk`, `did:peer`, `did:sov`, `did:indy` and `did:cheqd`. +- 🛂 **Extendable [DID](https://www.w3.org/TR/did-core/) resolver and registrar** - out of the box support for `did:web`, `did:key`, `did:jwk`, `did:peer`, `did:sov`, `did:indy`, `did:cheqd` and `did:hedera`. - 🔑 **[OpenID4VC](https://openid.net/sg/openid4vc/)** - support for [OpenID for Verifiable Credential Issuance](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html), [OpenID for Verifiable Presentations](https://openid.net/specs/openid-4-verifiable-presentations-1_0.html) and [Self-Issued OpenID Provider v2](https://openid.net/specs/openid-connect-self-issued-v2-1_0.html). - 🪪 **Multiple credential formats** - [W3C Verifiable Credential Data Model v1.1](https://www.w3.org/TR/vc-data-model/), [SD-JWT VCs](https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-03.html), and [AnonCreds](https://hyperledger.github.io/anoncreds-spec/). - 🏢 **Multi-tenant** - Optional multi-tenant module for managing multiple tenants under a single agent. @@ -158,6 +158,14 @@ See [Supported Features](https://credo.js.org/guides/features) on the Credo webs + + @credo-ts/hedera + + + @credo-ts/hedera version + + + @aries-framework/indy-sdk (deprecated, unmaintained after 0.4.x) diff --git a/demo/package.json b/demo/package.json index 17cc3b3caf..ecb205d9a7 100644 --- a/demo/package.json +++ b/demo/package.json @@ -13,19 +13,21 @@ "faber": "ts-node src/FaberInquirer.ts" }, "dependencies": { - "@hyperledger/indy-vdr-nodejs": "catalog:", "@hyperledger/anoncreds-nodejs": "catalog:", + "@hyperledger/indy-vdr-nodejs": "catalog:", "@openwallet-foundation/askar-nodejs": "catalog:", "inquirer": "^8.2.5" }, "devDependencies": { "@credo-ts/anoncreds": "workspace:*", "@credo-ts/askar": "workspace:*", + "@credo-ts/cheqd": "workspace:*", "@credo-ts/core": "workspace:*", "@credo-ts/didcomm": "workspace:*", + "@credo-ts/hedera": "workspace:*", "@credo-ts/indy-vdr": "workspace:*", - "@credo-ts/cheqd": "workspace:*", "@credo-ts/node": "workspace:*", + "@hiero-did-sdk/client": "0.1.3", "@types/figlet": "^1.5.4", "@types/inquirer": "^8.2.6", "clear": "^0.1.0", diff --git a/demo/src/BaseAgent.ts b/demo/src/BaseAgent.ts index 0f19b2fadd..837b79722a 100644 --- a/demo/src/BaseAgent.ts +++ b/demo/src/BaseAgent.ts @@ -1,7 +1,3 @@ -import type { InitConfig } from '@credo-ts/core' -import type { DidCommModuleConfigOptions } from '@credo-ts/didcomm' -import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' - import { AnonCredsCredentialFormatService, AnonCredsModule, @@ -19,7 +15,9 @@ import { CheqdModule, CheqdModuleConfig, } from '@credo-ts/cheqd' +import type { InitConfig } from '@credo-ts/core' import { Agent, DidsModule } from '@credo-ts/core' +import type { DidCommModuleConfigOptions } from '@credo-ts/didcomm' import { AutoAcceptCredential, AutoAcceptProof, @@ -31,13 +29,16 @@ import { V2ProofProtocol, getDefaultDidcommModules, } from '@credo-ts/didcomm' +import type { IndyVdrPoolConfig } from '@credo-ts/indy-vdr' import { IndyVdrAnonCredsRegistry, IndyVdrIndyDidResolver, IndyVdrModule } from '@credo-ts/indy-vdr' import { HttpInboundTransport, agentDependencies } from '@credo-ts/node' +import { HederaNetwork } from '@hiero-did-sdk/client' import { anoncreds } from '@hyperledger/anoncreds-nodejs' import { indyVdr } from '@hyperledger/indy-vdr-nodejs' import { askar } from '@openwallet-foundation/askar-nodejs' import { AskarModuleConfigStoreOptions } from '@credo-ts/askar' +import { HederaAnonCredsRegistry, HederaDidRegistrar, HederaDidResolver, HederaModule } from '@credo-ts/hedera' import { greenText } from './OutputClass' const bcovrin = `{"reqSignature":{},"txn":{"data":{"data":{"alias":"Node1","blskey":"4N8aUNHSgjQVgkpm8nhNEfDf6txHznoYREg9kirmJrkivgL4oSEimFF6nsQ6M41QvhM2Z33nves5vfSn9n1UwNFJBYtWVnHYMATn76vLuL3zU88KyeAYcHfsih3He6UHcXDxcaecHVz6jhCYz1P2UZn2bDVruL5wXpehgBfBaLKm3Ba","blskey_pop":"RahHYiCvoNCtPTrVtP7nMC5eTYrsUA8WjXbdhNc8debh1agE9bGiJxWBXYNFbnJXoXhWFMvyqhqhRoq737YQemH5ik9oL7R4NTTCz2LEZhkgLJzB3QRQqJyBNyv7acbdHrAT8nQ9UkLbaVL9NBpnWXBTw4LEMePaSHEw66RzPNdAX1","client_ip":"138.197.138.255","client_port":9702,"node_ip":"138.197.138.255","node_port":9701,"services":["VALIDATOR"]},"dest":"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv"},"metadata":{"from":"Th7MpTaRZVRYnPiabds81Y"},"type":"0"},"txnMetadata":{"seqNo":1,"txnId":"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62"},"ver":"1"} @@ -121,7 +122,7 @@ function getAskarAnonCredsIndyModules( ], }), anoncreds: new AnonCredsModule({ - registries: [new IndyVdrAnonCredsRegistry(), new CheqdAnonCredsRegistry()], + registries: [new IndyVdrAnonCredsRegistry(), new CheqdAnonCredsRegistry(), new HederaAnonCredsRegistry()], anoncreds, }), indyVdr: new IndyVdrModule({ @@ -140,12 +141,23 @@ function getAskarAnonCredsIndyModules( }) ), dids: new DidsModule({ - resolvers: [new IndyVdrIndyDidResolver(), new CheqdDidResolver()], - registrars: [new CheqdDidRegistrar()], + resolvers: [new IndyVdrIndyDidResolver(), new CheqdDidResolver(), new HederaDidResolver()], + registrars: [new CheqdDidRegistrar(), new HederaDidRegistrar()], }), askar: new AskarModule({ askar, store: askarStoreConfig, }), + hedera: new HederaModule({ + networks: [ + { + network: (process.env.HEDERA_NETWORK as HederaNetwork) ?? 'testnet', + operatorId: process.env.HEDERA_OPERATOR_ID ?? '0.0.5489553', + operatorKey: + process.env.HEDERA_OPERATOR_KEY ?? + '302e020100300506032b6570042204209f54b75b6238ced43e41b1463999cb40bf2f7dd2c9fd4fd3ef780027c016a138', + }, + ], + }), } as const } diff --git a/demo/src/Faber.ts b/demo/src/Faber.ts index b6523610cb..0ea91b03d7 100644 --- a/demo/src/Faber.ts +++ b/demo/src/Faber.ts @@ -14,6 +14,7 @@ import { Color, Output, greenText, purpleText, redText } from './OutputClass' export enum RegistryOptions { indy = 'did:indy', cheqd = 'did:cheqd', + hedera = 'did:hedera', } export class Faber extends BaseAgent { @@ -38,11 +39,22 @@ export class Faber extends BaseAgent { // and store the existing did in the wallet // indy did is based on private key (seed) const unqualifiedIndyDid = '2jEvRuKmfBJTRa7QowDpNN' - const cheqdDid = 'did:cheqd:testnet:d37eba59-513d-42d3-8f9f-d1df0548b675' - const indyDid = `did:indy:${indyNetworkConfig.indyNamespace}:${unqualifiedIndyDid}` - const didDocumentRelativeKeyId = registry === RegistryOptions.indy ? '#verkey' : '#key-1' - const did = registry === RegistryOptions.indy ? indyDid : cheqdDid + const rootKeyIds: Record = { + [RegistryOptions.indy]: '#verkey', + [RegistryOptions.cheqd]: '#key-1', + [RegistryOptions.hedera]: '#did-root-key', + } + + const Dids: Record = { + [RegistryOptions.indy]: `did:indy:${indyNetworkConfig.indyNamespace}:${unqualifiedIndyDid}`, + [RegistryOptions.cheqd]: 'did:cheqd:testnet:d37eba59-513d-42d3-8f9f-d1df0548b675', + [RegistryOptions.hedera]: 'did:hedera:testnet:44eesExqdsUvLZ35FpnBPErqRGRnYbzzyG3wgCCYxkmq_0.0.6231121', + } + + const didDocumentRelativeKeyId = rootKeyIds[registry] + const did = Dids[registry] + const { privateJwk } = transformPrivateKeyToPrivateJwk({ type: { crv: 'Ed25519', diff --git a/demo/src/FaberInquirer.ts b/demo/src/FaberInquirer.ts index 31d9958776..ac5fb6118e 100644 --- a/demo/src/FaberInquirer.ts +++ b/demo/src/FaberInquirer.ts @@ -90,7 +90,9 @@ export class FaberInquirer extends BaseInquirer { } public async credential() { - const registry = await prompt([this.inquireOptions([RegistryOptions.indy, RegistryOptions.cheqd])]) + const registry = await prompt([ + this.inquireOptions([RegistryOptions.indy, RegistryOptions.cheqd, RegistryOptions.hedera]), + ]) await this.faber.importDid(registry.options) await this.faber.issueCredential() const title = 'Is the credential offer accepted?' diff --git a/packages/hedera/CHANGELOG.md b/packages/hedera/CHANGELOG.md new file mode 100644 index 0000000000..cb65216ef8 --- /dev/null +++ b/packages/hedera/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## 0.5.13 + +### Patch Changes + +- Added Hedera module + diff --git a/packages/hedera/README.md b/packages/hedera/README.md new file mode 100644 index 0000000000..0f0db816da --- /dev/null +++ b/packages/hedera/README.md @@ -0,0 +1,31 @@ +

+
+ Credo Logo +

+

Credo Hedera Module

+

+ License + typescript + @credo-ts/hedera version + +

+
+ +Credo hedera provides integration of the Hedera network into Credo. See the [Hedera Setup](https://credo.js.org/guides/getting-started/set-up/hedera) for installation instructions. diff --git a/packages/hedera/jest.config.ts b/packages/hedera/jest.config.ts new file mode 100644 index 0000000000..d91513a189 --- /dev/null +++ b/packages/hedera/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from '@jest/types' + +import base from '../../jest.config.base' + +import packageJson from './package.json' + +const config: Config.InitialOptions = { + ...base, + displayName: packageJson.name, + setupFilesAfterEnv: ['./tests/setup.ts'], + coveragePathIgnorePatterns: ['../tests'], +} + +export default config diff --git a/packages/hedera/package.json b/packages/hedera/package.json new file mode 100644 index 0000000000..8532870728 --- /dev/null +++ b/packages/hedera/package.json @@ -0,0 +1,45 @@ +{ + "name": "@credo-ts/hedera", + "main": "src/index", + "types": "src/index", + "version": "0.5.13", + "files": ["build"], + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "homepage": "https://github.com/openwallet-foundation/credo-ts/tree/main/packages/hedera", + "repository": { + "type": "git", + "url": "https://github.com/openwallet-foundation/credo-ts", + "directory": "packages/hedera" + }, + "scripts": { + "build": "pnpm run clean && pnpm run compile", + "clean": "rimraf ./build", + "compile": "tsc -p tsconfig.build.json", + "prepublishOnly": "pnpm run build", + "test": "jest", + "test:coverage": "jest --coverage", + "style:check": "biome check --unsafe", + "style:fix": "biome check --write --unsafe" + }, + "dependencies": { + "@credo-ts/anoncreds": "workspace:*", + "@credo-ts/core": "workspace:*", + "@hashgraph/sdk": "^2.72.0", + "@hiero-did-sdk/anoncreds": "^0.1.3", + "@hiero-did-sdk/client": "^0.1.3", + "@hiero-did-sdk/core": "^0.1.3", + "@hiero-did-sdk/hcs": "^0.1.3", + "@hiero-did-sdk/publisher-internal": "^0.1.3", + "@hiero-did-sdk/registrar": "^0.1.3", + "@hiero-did-sdk/resolver": "^0.1.3" + }, + "devDependencies": { + "@credo-ts/node": "workspace:*", + "@hyperledger/anoncreds-nodejs": "^0.3.1", + "rimraf": "^4.4.0", + "zstd-napi": "^0.0.10" + } +} diff --git a/packages/hedera/src/HederaModule.ts b/packages/hedera/src/HederaModule.ts new file mode 100644 index 0000000000..c3ce218400 --- /dev/null +++ b/packages/hedera/src/HederaModule.ts @@ -0,0 +1,32 @@ +import { DependencyManager, Module } from '@credo-ts/core' + +import { AgentConfig, Buffer } from '@credo-ts/core' + +import { HederaModuleConfig, HederaModuleConfigOptions } from './HederaModuleConfig' +import { HederaLedgerService } from './ledger/HederaLedgerService' + +export class HederaModule implements Module { + public readonly config: HederaModuleConfig + + public constructor(config: HederaModuleConfigOptions) { + this.config = new HederaModuleConfig(config) + } + + public register(dependencyManager: DependencyManager) { + // Warn about experimental module + dependencyManager + .resolve(AgentConfig) + .logger.warn( + "The '@credo-ts/hedera' module is experimental and could have unexpected breaking changes. When using this module, make sure to use strict versions for all @credo-ts packages." + ) + + // Register config + dependencyManager.registerInstance(HederaModuleConfig, this.config) + dependencyManager.registerSingleton(HederaLedgerService) + + // Hedera module needs Buffer to be available globally + // If it is not available yet, we overwrite it with the + // Buffer implementation from Credo + global.Buffer = global.Buffer || Buffer + } +} diff --git a/packages/hedera/src/HederaModuleConfig.ts b/packages/hedera/src/HederaModuleConfig.ts new file mode 100644 index 0000000000..4b5aa6fab5 --- /dev/null +++ b/packages/hedera/src/HederaModuleConfig.ts @@ -0,0 +1,11 @@ +import { HederaAnoncredsRegistryConfiguration } from '@hiero-did-sdk/anoncreds' + +export interface HederaModuleConfigOptions extends HederaAnoncredsRegistryConfiguration {} + +export class HederaModuleConfig { + public readonly options: HederaModuleConfigOptions + + public constructor(options: HederaModuleConfigOptions) { + this.options = options + } +} diff --git a/packages/hedera/src/anoncreds/HederaAnonCredsRegistry.ts b/packages/hedera/src/anoncreds/HederaAnonCredsRegistry.ts new file mode 100644 index 0000000000..cff195cfd5 --- /dev/null +++ b/packages/hedera/src/anoncreds/HederaAnonCredsRegistry.ts @@ -0,0 +1,239 @@ +import type { + AnonCredsRegistry, + GetCredentialDefinitionReturn, + GetRevocationRegistryDefinitionReturn, + GetRevocationStatusListReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from '@credo-ts/anoncreds' +import type { AgentContext } from '@credo-ts/core' +import { HederaLedgerService } from '../ledger/HederaLedgerService' + +export class HederaAnonCredsRegistry implements AnonCredsRegistry { + public readonly methodName = 'hedera' + public readonly supportedIdentifier = /^did:hedera:.*$/ + + public async registerSchema( + agentContext: AgentContext, + options: RegisterSchemaOptions + ): Promise { + try { + agentContext.config.logger.trace('Registering schema on Hedera ledger') + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.registerSchema(agentContext, options) + } catch (error) { + agentContext.config.logger.debug(`Error registering schema for did '${options.schema.issuerId}'`, { + error, + did: options.schema.issuerId, + schema: options, + }) + return { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'failed', + schema: options.schema, + reason: `Unable to register schema: ${error.message}`, + }, + } + } + } + + public async getSchema(agentContext: AgentContext, schemaId: string): Promise { + try { + agentContext.config.logger.trace(`Resolving schema '${schemaId}' from Hedera ledger`) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.getSchema(agentContext, schemaId) + } catch (error) { + agentContext.config.logger.error(`Error retrieving schema '${schemaId}'`, { + error, + schemaId, + }) + return { + schemaId, + resolutionMetadata: { + error: 'notFound', + message: `Unable to resolve schema: ${error.message}`, + }, + schemaMetadata: {}, + } + } + } + + public async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + try { + agentContext.config.logger.trace('Registering credential definition on Hedera ledger') + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.registerCredentialDefinition(agentContext, options) + } catch (error) { + agentContext.config.logger.error( + `Error registering credential definition for did '${options.credentialDefinition.issuerId}'`, + { + error, + did: options.credentialDefinition.issuerId, + schema: options, + } + ) + return { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + state: 'failed', + credentialDefinition: options.credentialDefinition, + reason: `Unable to register credential definition: ${error.message}`, + }, + } + } + } + + public async getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + try { + agentContext.config.logger.trace(`Resolving credential definition '${credentialDefinitionId}' from Hedera ledger`) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.getCredentialDefinition(agentContext, credentialDefinitionId) + } catch (error) { + agentContext.config.logger.error(`Error retrieving credential definition '${credentialDefinitionId}'`, { + error, + credentialDefinitionId, + }) + return { + credentialDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `Unable to resolve credential definition: ${error.message}`, + }, + credentialDefinitionMetadata: {}, + } + } + } + + public async registerRevocationRegistryDefinition( + agentContext: AgentContext, + options: RegisterRevocationRegistryDefinitionOptions + ): Promise { + try { + agentContext.config.logger.trace( + `Registering revocation registry definition for '${options.revocationRegistryDefinition.credDefId}' on Hedera ledger` + ) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.registerRevocationRegistryDefinition(agentContext, options) + } catch (error) { + agentContext.config.logger.error( + `Error registering revocation registry definition for did '${options.revocationRegistryDefinition.issuerId}'`, + { + error, + did: options.revocationRegistryDefinition.issuerId, + options, + } + ) + return { + revocationRegistryDefinitionMetadata: {}, + registrationMetadata: {}, + revocationRegistryDefinitionState: { + state: 'failed', + revocationRegistryDefinition: options.revocationRegistryDefinition, + reason: `Unable to register revocation registry definition: ${error.message}`, + }, + } + } + } + + public async getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + try { + agentContext.config.logger.trace( + `Resolving revocation registry definition for '${revocationRegistryDefinitionId}' from Hedera ledger` + ) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.getRevocationRegistryDefinition(agentContext, revocationRegistryDefinitionId) + } catch (error) { + agentContext.config.logger.error( + `Error retrieving revocation registry definition '${revocationRegistryDefinitionId}'`, + { + error, + revocationRegistryDefinitionId, + } + ) + return { + revocationRegistryDefinitionId, + resolutionMetadata: { + error: 'notFound', + message: `Unable to resolve revocation registry definition: ${error.message}`, + }, + revocationRegistryDefinitionMetadata: {}, + } + } + } + + public async registerRevocationStatusList( + agentContext: AgentContext, + options: RegisterRevocationStatusListOptions + ): Promise { + try { + agentContext.config.logger.trace( + `Registering revocation status list for '${options.revocationStatusList.revRegDefId}' on Hedera ledger` + ) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.registerRevocationStatusList(agentContext, options) + } catch (error) { + agentContext.config.logger.error( + `Error registering revocation status list for did '${options.revocationStatusList.issuerId}'`, + { + error, + did: options.revocationStatusList.issuerId, + options, + } + ) + return { + revocationStatusListMetadata: {}, + registrationMetadata: {}, + revocationStatusListState: { + state: 'failed', + revocationStatusList: options.revocationStatusList, + reason: `Unable to register revocation status list: ${error.message}`, + }, + } + } + } + + public async getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + try { + agentContext.config.logger.trace( + `Resolving revocation status for for '${revocationRegistryId}' from Hedera ledger` + ) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + return await ledgerService.getRevocationStatusList(agentContext, revocationRegistryId, timestamp * 1000) + } catch (error) { + agentContext.config.logger.error(`Error retrieving revocation registry status list '${revocationRegistryId}'`, { + error, + revocationRegistryId, + }) + return { + resolutionMetadata: { + error: 'notFound', + message: `Unable to resolve revocation registry status list: ${error.message}`, + }, + revocationStatusListMetadata: {}, + } + } + } +} diff --git a/packages/hedera/src/anoncreds/index.ts b/packages/hedera/src/anoncreds/index.ts new file mode 100644 index 0000000000..886cabd90b --- /dev/null +++ b/packages/hedera/src/anoncreds/index.ts @@ -0,0 +1 @@ +export { HederaAnonCredsRegistry } from './HederaAnonCredsRegistry' diff --git a/packages/hedera/src/dids/HederaDidRegistrar.ts b/packages/hedera/src/dids/HederaDidRegistrar.ts new file mode 100644 index 0000000000..f68a5a234a --- /dev/null +++ b/packages/hedera/src/dids/HederaDidRegistrar.ts @@ -0,0 +1,182 @@ +import { + AgentContext, + DidCreateResult, + DidDeactivateResult, + DidDocument, + DidDocumentKey, + DidDocumentRole, + DidDocumentService, + DidRecord, + DidRegistrar, + DidRepository, + DidUpdateResult, + JsonTransformer, +} from '@credo-ts/core' +import { + HederaDidCreateOptions, + HederaDidDeactivateOptions, + HederaDidUpdateOptions, + HederaLedgerService, +} from '../ledger/HederaLedgerService' + +export class HederaDidRegistrar implements DidRegistrar { + public readonly supportedMethods = ['hedera'] + + async create(agentContext: AgentContext, options: HederaDidCreateOptions): Promise { + try { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + + const { did, didDocument, rootKey } = await ledgerService.createDid(agentContext, options) + + const credoDidDocument = new DidDocument({ + ...didDocument, + service: didDocument.service?.map((s) => new DidDocumentService(s)), + }) + + await didRepository.save( + agentContext, + new DidRecord({ + did, + role: DidDocumentRole.Created, + didDocument: credoDidDocument, + keys: [rootKey], + }) + ) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: credoDidDocument, + }, + } + } catch (error) { + agentContext.config.logger.debug('Error creating DID', { + error, + }) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Unable to register Did: ${error.message}`, + }, + } + } + } + + async update(agentContext: AgentContext, options: HederaDidUpdateOptions): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + + try { + const { did } = options + const { didDocument, didDocumentMetadata } = await ledgerService.resolveDid(agentContext, did) + const didRecord = await didRepository.findCreatedDid(agentContext, did) + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + + const keys = this.concatKeys(didRecord.keys, options.secret?.keys) + const { didDocument: updatedDidDocument } = await ledgerService.updateDid(agentContext, { + ...options, + secret: { keys }, + }) + + didRecord.didDocument = JsonTransformer.fromJSON(updatedDidDocument, DidDocument) + didRecord.keys = keys + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: didRecord.didDocument, + }, + } + } catch (error) { + agentContext.config.logger.error('Error updating DID', error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Unable update DID: ${error.message}`, + }, + } + } + } + + async deactivate( + agentContext: AgentContext, + options: Omit + ): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + + const did = options.did + + try { + const { didDocument, didDocumentMetadata } = await ledgerService.resolveDid(agentContext, did) + + const didRecord = await didRepository.findCreatedDid(agentContext, did) + + if (!didDocument || didDocumentMetadata.deactivated || !didRecord) { + return { + didDocumentMetadata, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: 'Did not found', + }, + } + } + const { didDocument: deactivatedDidDocument } = await ledgerService.deactivateDid(agentContext, { + ...options, + secret: { keys: didRecord.keys }, + }) + + didRecord.didDocument = JsonTransformer.fromJSON(deactivatedDidDocument, DidDocument) + await didRepository.update(agentContext, didRecord) + + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'finished', + did, + didDocument: didRecord.didDocument, + }, + } + } catch (error) { + agentContext.config.logger.error('Error deactivating DID', error) + return { + didDocumentMetadata: {}, + didRegistrationMetadata: {}, + didState: { + state: 'failed', + reason: `Unable deactivating DID: ${error.message}`, + }, + } + } + } + + private concatKeys(keys1: DidDocumentKey[] = [], keys2: DidDocumentKey[] = []): DidDocumentKey[] { + return [ + ...keys1, + ...keys2.filter((k2) => !keys1.some((k1) => k1.didDocumentRelativeKeyId === k2.didDocumentRelativeKeyId)), + ] + } +} diff --git a/packages/hedera/src/dids/HederaDidResolver.ts b/packages/hedera/src/dids/HederaDidResolver.ts new file mode 100644 index 0000000000..1a0a241bdb --- /dev/null +++ b/packages/hedera/src/dids/HederaDidResolver.ts @@ -0,0 +1,48 @@ +import { + type AgentContext, + DidDocument, + type DidResolutionOptions, + type DidResolutionResult, + type DidResolver, + JsonTransformer, + type ParsedDid, +} from '@credo-ts/core' +import { HederaLedgerService } from '../ledger/HederaLedgerService' + +export class HederaDidResolver implements DidResolver { + public readonly supportedMethods = ['hedera'] + public readonly allowsCaching = true + public readonly allowsLocalDidRecord = true + + async resolve( + agentContext: AgentContext, + did: string, + _parsed: ParsedDid, + _didResolutionOptions: DidResolutionOptions + ): Promise { + try { + agentContext.config.logger.trace('Try to resolve a did document from ledger') + const ledgerService = agentContext.dependencyManager.resolve(HederaLedgerService) + const resolveDidResult = await ledgerService.resolveDid(agentContext, did) + const didDocument = JsonTransformer.fromJSON(resolveDidResult.didDocument, DidDocument) + return { + didDocument, + didDocumentMetadata: resolveDidResult.didDocumentMetadata, + didResolutionMetadata: resolveDidResult.didResolutionMetadata, + } + } catch (error) { + agentContext.config.logger.debug('Error resolving the did', { + error, + did, + }) + return { + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `Unable to resolve did '${did}': ${error}`, + }, + } + } + } +} diff --git a/packages/hedera/src/dids/index.ts b/packages/hedera/src/dids/index.ts new file mode 100644 index 0000000000..960467c6ea --- /dev/null +++ b/packages/hedera/src/dids/index.ts @@ -0,0 +1,2 @@ +export { HederaDidRegistrar } from './HederaDidRegistrar' +export { HederaDidResolver } from './HederaDidResolver' diff --git a/packages/hedera/src/index.ts b/packages/hedera/src/index.ts new file mode 100644 index 0000000000..47a772c75f --- /dev/null +++ b/packages/hedera/src/index.ts @@ -0,0 +1,4 @@ +export * from './dids' +export * from './anoncreds' +export * from './HederaModule' +export * from './HederaModuleConfig' diff --git a/packages/hedera/src/ledger/HederaLedgerService.ts b/packages/hedera/src/ledger/HederaLedgerService.ts new file mode 100644 index 0000000000..eac07c60c3 --- /dev/null +++ b/packages/hedera/src/ledger/HederaLedgerService.ts @@ -0,0 +1,537 @@ +import { + GetCredentialDefinitionReturn, + GetRevocationRegistryDefinitionReturn, + GetRevocationStatusListReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from '@credo-ts/anoncreds' +import { + type AgentContext, + DidCreateOptions, + DidDeactivateOptions, + type DidDocument, + DidDocumentKey, + DidRepository, + DidUpdateOptions, + Kms, + injectable, +} from '@credo-ts/core' +import { KeyManagementApi } from '@credo-ts/core/src/modules/kms' +import { Client, PrivateKey } from '@hashgraph/sdk' +import { HederaAnoncredsRegistry } from '@hiero-did-sdk/anoncreds' +import { HederaClientService, HederaNetwork } from '@hiero-did-sdk/client' +import { DIDResolution, DID_ROOT_KEY_ID, Service, VerificationMethod, parseDID } from '@hiero-did-sdk/core' +import { + CreateDIDResult, + DIDUpdateBuilder, + DeactivateDIDResult, + UpdateDIDResult, + generateCreateDIDRequest, + generateDeactivateDIDRequest, + generateUpdateDIDRequest, + submitCreateDIDRequest, + submitDeactivateDIDRequest, + submitUpdateDIDRequest, +} from '@hiero-did-sdk/registrar' +import { TopicReaderHederaHcs, resolveDID } from '@hiero-did-sdk/resolver' +import { HederaModuleConfig } from '../HederaModuleConfig' +import { CredoCache } from './cache/CredoCache' +import { KmsPublisher } from './publisher/KmsPublisher' +import { createOrGetKey, getMultibasePublicKey } from './utils' + +export interface HederaDidCreateOptions extends DidCreateOptions { + method: 'hedera' + options?: { + network?: HederaNetwork | string + } + secret?: { + rootKeyId?: string + keys?: DidDocumentKey[] + } +} + +export interface HederaCreateDIDResult extends CreateDIDResult { + rootKey: DidDocumentKey +} + +export interface HederaDidUpdateOptions extends DidUpdateOptions { + secret?: { + keys?: DidDocumentKey[] + } +} + +export interface HederaDidDeactivateOptions extends DidDeactivateOptions { + secret?: { + keys?: DidDocumentKey[] + } +} + +@injectable() +export class HederaLedgerService { + private readonly clientService: HederaClientService + + public constructor(private readonly config: HederaModuleConfig) { + this.clientService = new HederaClientService(config.options) + } + + public async resolveDid(agentContext: AgentContext, did: string): Promise { + const topicReader = this.getHederaHcsTopicReader(agentContext) + return await resolveDID(did, 'application/ld+json;profile="https://w3id.org/did-resolution"', { topicReader }) + } + + public async createDid(agentContext: AgentContext, props: HederaDidCreateOptions): Promise { + const { options, secret, didDocument } = props + return this.clientService.withClient({ networkName: options?.network }, async (client: Client) => { + const topicReader = this.getHederaHcsTopicReader(agentContext) + + const controller = + typeof didDocument?.controller === 'string' + ? didDocument?.controller + : Array.isArray(didDocument?.controller) + ? didDocument?.controller[0] + : undefined + + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + + const { keyId, publicJwk } = await createOrGetKey(kms, secret?.rootKeyId) + const rootKey = { kmsKeyId: keyId, didDocumentRelativeKeyId: DID_ROOT_KEY_ID } + + const publisher = await this.getPublisher(agentContext, client, keyId) + + const { state, signingRequest } = await generateCreateDIDRequest( + { + controller, + multibasePublicKey: getMultibasePublicKey(publicJwk), + topicReader, + }, + { + client, + publisher, + } + ) + + const signatureResult = await kms.sign({ keyId, data: signingRequest.serializedPayload, algorithm: 'EdDSA' }) + const createDidDocumentResult = await submitCreateDIDRequest( + { state, signature: signatureResult.signature, topicReader }, + { + client, + publisher, + } + ) + + if (didDocument) { + const keys = [...(secret?.keys ?? []), ...[rootKey]] + const updateDidDocumentResult = await this.updateDid(agentContext, { + did: createDidDocumentResult.did, + didDocumentOperation: 'setDidDocument', + didDocument, + options: { ...options }, + secret: { keys }, + }) + return { + ...updateDidDocumentResult, + rootKey, + } + } + + return { + ...createDidDocumentResult, + rootKey, + } + }) + } + + public async updateDid(agentContext: AgentContext, props: HederaDidUpdateOptions): Promise { + const { did, didDocumentOperation, didDocument, secret } = props + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + + if (!didDocumentOperation) { + throw new Error('DidDocumentOperation is required') + } + + const rootKey = secret?.keys?.find((key) => key.didDocumentRelativeKeyId === DID_ROOT_KEY_ID) + if (!rootKey?.kmsKeyId) { + throw new Error('The root key not found in the KMS') + } + + this.validateDidUpdateKeys(didDocument, secret?.keys ?? []) + + const { network: networkName } = parseDID(did) + return this.clientService.withClient({ networkName }, async (client: Client) => { + const topicReader = this.getHederaHcsTopicReader(agentContext) + + const currentDidDocumentResolution = await resolveDID( + did, + 'application/ld+json;profile="https://w3id.org/did-resolution"', + { topicReader } + ) + if (!currentDidDocumentResolution.didDocument) { + throw new Error(`DID ${did} not found`) + } + + const didUpdates = this.prepareDidUpdates( + currentDidDocumentResolution.didDocument, + didDocument, + didDocumentOperation + ) + + const publisher = await this.getPublisher(agentContext, client, rootKey.kmsKeyId) + + const { states, signingRequests } = await generateUpdateDIDRequest( + { + did, + updates: didUpdates.build(), + topicReader, + }, + { + client, + publisher, + } + ) + + const signatures = await this.signRequests(signingRequests, kms, rootKey.kmsKeyId) + return await submitUpdateDIDRequest( + { + states, + signatures, + topicReader, + }, + { + client, + publisher, + } + ) + }) + } + + public async deactivateDid( + agentContext: AgentContext, + props: HederaDidDeactivateOptions + ): Promise { + const { did, secret } = props + + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + + const rootKey = secret?.keys?.find((key) => key.didDocumentRelativeKeyId === DID_ROOT_KEY_ID) + if (!rootKey?.kmsKeyId) { + throw new Error('The root key not found in the KMS') + } + + const { network: networkName } = parseDID(props.did) + return this.clientService.withClient({ networkName }, async (client: Client) => { + const topicReader = this.getHederaHcsTopicReader(agentContext) + + const publisher = await this.getPublisher(agentContext, client, rootKey.kmsKeyId) + + const { state, signingRequest } = await generateDeactivateDIDRequest( + { + did, + topicReader, + }, + { + client, + publisher, + } + ) + const signatureResult = await kms.sign({ + keyId: rootKey.kmsKeyId, + data: signingRequest.serializedPayload, + algorithm: 'EdDSA', + }) + return await submitDeactivateDIDRequest( + { + state, + signature: signatureResult.signature, + topicReader, + }, + { + client, + publisher, + } + ) + }) + } + + getSchema(agentContext: AgentContext, schemaId: string): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + return registry.getSchema(schemaId) + } + + async registerSchema(agentContext: AgentContext, options: RegisterSchemaOptions): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + const issuerPrivateKey = await this.getIssuerPrivateKey(agentContext, options.schema.issuerId) + return registry.registerSchema({ ...options, issuerKeyDer: issuerPrivateKey.toStringDer() }) + } + + getCredentialDefinition( + agentContext: AgentContext, + credentialDefinitionId: string + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + return registry.getCredentialDefinition(credentialDefinitionId) + } + + async registerCredentialDefinition( + agentContext: AgentContext, + options: RegisterCredentialDefinitionOptions + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + const issuerPrivateKey = await this.getIssuerPrivateKey(agentContext, options.credentialDefinition.issuerId) + return await registry.registerCredentialDefinition({ + ...options, + issuerKeyDer: issuerPrivateKey.toStringDer(), + options: { + supportRevocation: !!options.options?.supportRevocation, + }, + }) + } + + getRevocationRegistryDefinition( + agentContext: AgentContext, + revocationRegistryDefinitionId: string + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + return registry.getRevocationRegistryDefinition(revocationRegistryDefinitionId) + } + + async registerRevocationRegistryDefinition( + agentContext: AgentContext, + options: RegisterRevocationRegistryDefinitionOptions + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + const issuerPrivateKey = await this.getIssuerPrivateKey(agentContext, options.revocationRegistryDefinition.issuerId) + return await registry.registerRevocationRegistryDefinition({ + ...options, + issuerKeyDer: issuerPrivateKey.toStringDer(), + }) + } + + getRevocationStatusList( + agentContext: AgentContext, + revocationRegistryId: string, + timestamp: number + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + return registry.getRevocationStatusList(revocationRegistryId, timestamp) + } + + async registerRevocationStatusList( + agentContext: AgentContext, + options: RegisterRevocationStatusListOptions + ): Promise { + const registry = this.getHederaAnonCredsRegistry(agentContext) + const issuerPrivateKey = await this.getIssuerPrivateKey(agentContext, options.revocationStatusList.issuerId) + return await registry.registerRevocationStatusList({ + ...options, + issuerKeyDer: issuerPrivateKey.toStringDer(), + }) + } + + private getHederaHcsTopicReader(agentContext: AgentContext): TopicReaderHederaHcs { + const cache = this.config.options.cache ?? new CredoCache(agentContext) + return new TopicReaderHederaHcs({ ...this.config.options, cache }) + } + + private async getPublisher(agentContext: AgentContext, client: Client, keyId: string): Promise { + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + const key = await createOrGetKey(kms, keyId) + return new KmsPublisher(agentContext, client, key) + } + + private getHederaAnonCredsRegistry(agentContext: AgentContext): HederaAnoncredsRegistry { + const cache = this.config.options.cache ?? new CredoCache(agentContext) + return new HederaAnoncredsRegistry({ ...this.config.options, cache }) + } + + private getDidDocumentEntryId(item: { id: string } | string): string { + const id = typeof item === 'string' ? item : item.id + return id.includes('#') ? `#${id.split('#').pop()}` : id + } + + private getDidDocumentPropertyDiff( + originalEntries: Array = [], + updatedEntries: Array = [] + ) { + const originalIds = new Set(originalEntries.map((item) => this.getDidDocumentEntryId(item))) + const updatedIds = new Set(updatedEntries.map((item) => this.getDidDocumentEntryId(item))) + + const unchangedEntries = updatedEntries.filter((item) => originalIds.has(this.getDidDocumentEntryId(item))) + const newEntries = updatedEntries.filter((item) => !originalIds.has(this.getDidDocumentEntryId(item))) + const removedEntries = originalEntries.filter((item) => !updatedIds.has(this.getDidDocumentEntryId(item))) + + return { unchangedEntries, newEntries, removedEntries } + } + + private async signRequests( + signingRequests: Record, + kms: KeyManagementApi, + keyId: string + ): Promise> { + const result: Record = {} + + for (const [key, request] of Object.entries(signingRequests)) { + const { signature } = await kms.sign({ + keyId, + data: request.serializedPayload, + algorithm: 'EdDSA', + }) + result[key] = signature + } + + return result + } + + private validateDidUpdateKeys(didDocument: DidDocument | Partial, keys: DidDocumentKey[]) { + const verificationRelationships = [ + 'verificationMethod', + 'assertionMethod', + 'authentication', + 'capabilityDelegation', + 'capabilityInvocation', + 'keyAgreement', + ] as const + + for (const relationship of verificationRelationships) { + const entries = didDocument[relationship] + if (!entries) continue + + for (const entry of entries) { + const id = this.getDidDocumentEntryId(entry) + if (!keys.some((key) => key.didDocumentRelativeKeyId === id)) { + throw new Error( + `Key ${id} is present in updated DID Document, but missing from DID record keys and DID update arguments` + ) + } + } + } + } + + private prepareDidUpdates( + originalDocument: DidDocument | Partial, + newDocument: DidDocument | Partial, + operation: string + ): DIDUpdateBuilder { + const builder = new DIDUpdateBuilder() + const properties = [ + 'service', + 'verificationMethod', + 'assertionMethod', + 'authentication', + 'capabilityDelegation', + 'capabilityInvocation', + 'keyAgreement', + ] as const + + for (const property of properties) { + const { unchangedEntries, newEntries, removedEntries } = this.getDidDocumentPropertyDiff( + originalDocument[property], + newDocument[property] + ) + + if (operation === 'setDidDocument') { + for (const entry of removedEntries) { + const entryId = this.getDidDocumentEntryId(entry) + if (entryId === DID_ROOT_KEY_ID) continue + const builderMethod = this.getUpdateMethod(builder, property, 'remove') + builderMethod(entryId) + } + + for (const entry of newEntries) { + if (this.getDidDocumentEntryId(entry) === DID_ROOT_KEY_ID) continue + const builderMethod = this.getUpdateMethod(builder, property, 'add') + builderMethod(entry) + } + } + + if (operation === 'addToDidDocument') { + for (const entry of newEntries) { + if (this.getDidDocumentEntryId(entry) === DID_ROOT_KEY_ID) continue + const builderMethod = this.getUpdateMethod(builder, property, 'add') + builderMethod(entry) + } + } + + if (operation === 'removeFromDidDocument') { + for (const entry of unchangedEntries) { + const entryId = this.getDidDocumentEntryId(entry) + if (entryId === DID_ROOT_KEY_ID) continue + const builderMethod = this.getUpdateMethod(builder, property, 'remove') + builderMethod(entryId) + } + } + } + + return builder + } + + private getUpdateMethod( + builder: DIDUpdateBuilder, + property: string, + action: 'add' | 'remove' + // biome-ignore lint/suspicious/noExplicitAny: + ): (item: any) => DIDUpdateBuilder { + // biome-ignore lint/suspicious/noExplicitAny: + const methodMap: Record DIDUpdateBuilder>> = { + service: { + add: (item: Service) => builder.addService(item), + remove: (id: string) => builder.removeService(id), + }, + verificationMethod: { + add: (item: VerificationMethod | string) => builder.addVerificationMethod(item), + remove: (id: string) => builder.removeVerificationMethod(id), + }, + assertionMethod: { + add: (item: VerificationMethod | string) => builder.addAssertionMethod(item), + remove: (id: string) => builder.removeAssertionMethod(id), + }, + authentication: { + add: (item: VerificationMethod | string) => builder.addAuthenticationMethod(item), + remove: (id: string) => builder.removeAuthenticationMethod(id), + }, + capabilityDelegation: { + add: (item: VerificationMethod | string) => builder.addCapabilityDelegationMethod(item), + remove: (id: string) => builder.removeCapabilityDelegationMethod(id), + }, + capabilityInvocation: { + add: (item: VerificationMethod | string) => builder.addCapabilityInvocationMethod(item), + remove: (id: string) => builder.removeCapabilityInvocationMethod(id), + }, + keyAgreement: { + add: (item: VerificationMethod | string) => builder.addKeyAgreementMethod(item), + remove: (id: string) => builder.removeKeyAgreementMethod(id), + }, + } + + const propertyMethods = methodMap[property] + if (!propertyMethods) { + return () => builder + } + + return propertyMethods[action] + } + + private async getIssuerPrivateKey(agentContext: AgentContext, issuerId: string): Promise { + const didRepository = agentContext.dependencyManager.resolve(DidRepository) + const kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + + const didRecord = await didRepository.findCreatedDid(agentContext, issuerId) + const rootKey = didRecord?.keys?.find((key) => key.didDocumentRelativeKeyId === DID_ROOT_KEY_ID) + if (!rootKey?.kmsKeyId) { + throw new Error('The root key not found in the KMS') + } + + // @ts-ignore + const keyManagementService = kms.getKms(agentContext, 'askar') + // @ts-ignore + const keyInfo = await keyManagementService.getKeyAsserted(agentContext, rootKey.kmsKeyId) + + return PrivateKey.fromBytesED25519(keyInfo.key.secretBytes) + } +} diff --git a/packages/hedera/src/ledger/cache/CredoCache.ts b/packages/hedera/src/ledger/cache/CredoCache.ts new file mode 100644 index 0000000000..bd6a2a634e --- /dev/null +++ b/packages/hedera/src/ledger/cache/CredoCache.ts @@ -0,0 +1,34 @@ +import { AgentContext, CacheModuleConfig, CredoError } from '@credo-ts/core' +import { Cache as CoreCredoCache } from '@credo-ts/core' +import { Cache } from '@hiero-did-sdk/core' + +export class CredoCache implements Cache { + private readonly credoCache: CoreCredoCache + + constructor(private readonly agentContext: AgentContext) { + this.credoCache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache + if (!this.credoCache) { + throw new CredoError('Failed to initialize cache: Credo cache instance is not found in dependency manager') + } + } + + async get(key: string): Promise { + return await this.credoCache.get(this.agentContext, key) + } + + async set(key: string, value: CacheValue, _expiresInSeconds?: number): Promise { + await this.credoCache.set(this.agentContext, key, value) + } + + async remove(key: string): Promise { + await this.credoCache.remove(this.agentContext, key) + } + + async clear(): Promise { + // no-op + } + + async cleanupExpired(): Promise { + // no-op + } +} diff --git a/packages/hedera/src/ledger/publisher/KmsPublisher.ts b/packages/hedera/src/ledger/publisher/KmsPublisher.ts new file mode 100644 index 0000000000..9de25caca5 --- /dev/null +++ b/packages/hedera/src/ledger/publisher/KmsPublisher.ts @@ -0,0 +1,55 @@ +import { AgentContext, Kms, TypedArrayEncoder } from '@credo-ts/core' +import { KeyManagementApi, KmsJwkPublicOkp } from '@credo-ts/core/src/modules/kms' +import { Client, PublicKey, Transaction, TransactionReceipt } from '@hashgraph/sdk' +import { KeysUtility } from '@hiero-did-sdk/core' +import { Publisher as ClientPublisher } from '@hiero-did-sdk/publisher-internal' +import { createOrGetKey } from '../utils' + +export class KmsPublisher extends ClientPublisher { + private readonly kms: KeyManagementApi + + private keyId: string + private submitPublicKey: PublicKey + + constructor( + agentContext: AgentContext, + client: Client, + key: { keyId: string; publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } } + ) { + super(client) + + this.kms = agentContext.dependencyManager.resolve(Kms.KeyManagementApi) + + this.keyId = key.keyId + this.submitPublicKey = KeysUtility.fromBytes( + Uint8Array.from(TypedArrayEncoder.fromBase64(key.publicJwk.x)) + ).toPublicKey() + } + + async setKeyId(keyId: string) { + this.keyId = keyId + + const { publicJwk } = await createOrGetKey(this.kms, keyId) + + this.submitPublicKey = KeysUtility.fromBytes( + Uint8Array.from(TypedArrayEncoder.fromBase64(publicJwk.x)) + ).toPublicKey() + } + + publicKey(): PublicKey { + return this.submitPublicKey + } + + async publish(transaction: Transaction): Promise { + const frozenTransaction = transaction.freezeWith(this.client) + + await frozenTransaction.signWith(this.submitPublicKey, async (message) => { + const signatureResult = await this.kms.sign({ keyId: this.keyId, data: message, algorithm: 'EdDSA' }) + return signatureResult.signature + }) + + const response = await transaction.execute(this.client) + + return response.getReceipt(this.client) + } +} diff --git a/packages/hedera/src/ledger/utils/index.ts b/packages/hedera/src/ledger/utils/index.ts new file mode 100644 index 0000000000..3d99837ae8 --- /dev/null +++ b/packages/hedera/src/ledger/utils/index.ts @@ -0,0 +1,41 @@ +import { Kms, TypedArrayEncoder } from '@credo-ts/core' +import { KeyManagementApi, KmsJwkPublicOkp } from '@credo-ts/core/src/modules/kms' + +export const getMultibasePublicKey = (publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' }): string => { + return `z${TypedArrayEncoder.toBase58(Uint8Array.from(TypedArrayEncoder.fromBase64(publicJwk.x)))}` +} + +export const createOrGetKey = async ( + kms: KeyManagementApi, + keyId?: string +): Promise<{ keyId: string; publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } }> => { + if (!keyId) { + const createKeyResult = await kms.createKey({ + type: { + crv: 'Ed25519', + kty: 'OKP', + }, + }) + return { + publicJwk: createKeyResult.publicJwk, + keyId: createKeyResult.keyId, + } + } + + const publicJwk = await kms.getPublicKey({ keyId }) + if (!publicJwk) { + throw new Error(`Key with key id '${keyId}' not found`) + } + if (publicJwk.kty !== 'OKP' || publicJwk.crv !== 'Ed25519') { + throw new Error( + `Key with key id '${keyId}' uses unsupported ${Kms.getJwkHumanDescription(publicJwk)} for did:hedera` + ) + } + return { + keyId, + publicJwk: { + ...publicJwk, + crv: publicJwk.crv, + }, + } +} diff --git a/packages/hedera/tests/integration/hedera-anoncreds-registry.e2e.test.ts b/packages/hedera/tests/integration/hedera-anoncreds-registry.e2e.test.ts new file mode 100644 index 0000000000..83d828163a --- /dev/null +++ b/packages/hedera/tests/integration/hedera-anoncreds-registry.e2e.test.ts @@ -0,0 +1,168 @@ +import { Agent, ConsoleLogger, InMemoryLruCache, LogLevel, utils } from '@credo-ts/core' +import { HederaDidCreateOptions } from '../../src/ledger/HederaLedgerService' +import { getHederaAgent } from './utils' + +describe('Hedera AnonCreds support', () => { + let agent: Agent + let issuerId: string + + const logger = new ConsoleLogger(LogLevel.fatal) + const cache = new InMemoryLruCache({ limit: 10 }) + + beforeAll(async () => { + agent = getHederaAgent({ + label: 'alice', + logger, + cache, + }) + await agent.initialize() + + const didRegistrarResult = await agent.dids.create({ + method: 'hedera', + }) + if (!didRegistrarResult.didState?.didDocument?.id) throw new Error('DidRegistrarError') + + issuerId = didRegistrarResult.didState.didDocument.id + logger.debug('issuerId', [issuerId]) + }) + + beforeEach(() => { + cache.clear() + }) + + afterAll(async () => { + // Wait for messages to flush out + await new Promise((r) => setTimeout(r, 1000)) + + if (agent) { + await agent.shutdown() + } + }) + + describe('Hedera Anoncreds Registry', () => { + it('should execute the full workflow (register and resolve schema, credential definition, revocation registry definition, revocation status list)', async () => { + const schemaResult = await agent.modules.anoncreds.registerSchema({ + schema: { + name: utils.uuid(), + version: '1', + issuerId: issuerId, + attrNames: ['field1'], + }, + options: {}, + }) + logger.debug('RegisterSchema', [schemaResult]) + + const schemaId = schemaResult?.schemaState?.schemaId + expect(schemaId).toBeDefined() + + const credDefResult = await agent.modules.anoncreds.registerCredentialDefinition({ + credentialDefinition: { + tag: 'default', + issuerId: issuerId, + schemaId: schemaId, + }, + options: { + supportRevocation: true, + }, + }) + + logger.debug('credDefResult', [credDefResult]) + expect(credDefResult?.credentialDefinitionState?.state).toEqual('finished') + + const credentialDefinitionId = credDefResult.credentialDefinitionState.credentialDefinitionId ?? '' + + const revRegDefRegResult = await agent.modules.anoncreds.registerRevocationRegistryDefinition({ + revocationRegistryDefinition: { + issuerId: issuerId, + credentialDefinitionId, + maximumCredentialNumber: 10, + tag: 'default', + }, + options: {}, + }) + logger.debug('revRegDefRegResult', [revRegDefRegResult]) + const revocationRegistryDefinitionId = + revRegDefRegResult?.revocationRegistryDefinitionState?.revocationRegistryDefinitionId ?? '' + expect(revocationRegistryDefinitionId).toBeDefined() + + const resolvedRevRegDef = + await agent.modules.anoncreds.getRevocationRegistryDefinition(revocationRegistryDefinitionId) + expect(resolvedRevRegDef.revocationRegistryDefinitionId).toEqual(revocationRegistryDefinitionId) + + const registerRevocationStatusListResponse = await agent.modules.anoncreds.registerRevocationStatusList({ + options: {}, + revocationStatusList: { + issuerId: issuerId, + revocationRegistryDefinitionId, + }, + }) + logger.debug('registerRevocationStatusListResponse', [registerRevocationStatusListResponse]) + const revocationStatusList = registerRevocationStatusListResponse?.revocationStatusListState.revocationStatusList + expect(revocationStatusList).toBeDefined() + + const revocationStatusListResponse = await agent.modules.anoncreds.getRevocationStatusList( + revocationRegistryDefinitionId, + Date.now() / 1000 + ) + logger.debug('revocationStatusListResponse', [revocationStatusListResponse]) + + expect(revocationStatusListResponse?.revocationStatusList?.revRegDefId).toEqual(revocationRegistryDefinitionId) + expect(revocationStatusListResponse?.revocationStatusList?.issuerId).toEqual(issuerId) + expect(revocationStatusListResponse?.revocationStatusList?.revocationList).toEqual([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + + // Update revocation status list - Revoke indexes + const revokeUpdateRevocationStatusListResponse = await agent.modules.anoncreds.updateRevocationStatusList({ + options: {}, + revocationStatusList: { + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + issuedCredentialIndexes: undefined, + revokedCredentialIndexes: [1, 3, 5, 9], + }, + }) + logger.debug('revokeUpdateRevocationStatusListResponse', [revokeUpdateRevocationStatusListResponse]) + const revokeRevocationStatusList = + revokeUpdateRevocationStatusListResponse?.revocationStatusListState.revocationStatusList + expect(revokeRevocationStatusList).toBeDefined() + + const revokeRevocationStatusListResponse = await agent.modules.anoncreds.getRevocationStatusList( + revocationRegistryDefinitionId, + Date.now() / 1000 + ) + logger.debug('revokeRevocationStatusListResponse', [revokeRevocationStatusListResponse]) + expect(revokeRevocationStatusListResponse?.revocationStatusList?.revRegDefId).toEqual( + revocationRegistryDefinitionId + ) + expect(revokeRevocationStatusListResponse?.revocationStatusList?.issuerId).toEqual(issuerId) + expect(revokeRevocationStatusListResponse?.revocationStatusList?.revocationList).toEqual([ + 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, + ]) + + // Update revocation status list - Revoke/Issue indexes + const issueUpdateRevocationStatusListResponse = await agent.modules.anoncreds.updateRevocationStatusList({ + options: {}, + revocationStatusList: { + revocationRegistryDefinitionId: revocationRegistryDefinitionId, + issuedCredentialIndexes: [3, 5], + revokedCredentialIndexes: [4], + }, + }) + logger.debug('issueUpdateRevocationStatusListResponse', [issueUpdateRevocationStatusListResponse]) + const issueRevocationStatusList = + issueUpdateRevocationStatusListResponse?.revocationStatusListState.revocationStatusList + expect(issueRevocationStatusList).toBeDefined() + + const issueRevocationStatusListResponse = await agent.modules.anoncreds.getRevocationStatusList( + revocationRegistryDefinitionId, + Date.now() / 1000 + ) + logger.debug('issueRevocationStatusListResponse', [issueRevocationStatusListResponse]) + expect(issueRevocationStatusListResponse?.revocationStatusList?.revRegDefId).toEqual( + revocationRegistryDefinitionId + ) + expect(issueRevocationStatusListResponse?.revocationStatusList?.issuerId).toEqual(issuerId) + expect(issueRevocationStatusListResponse?.revocationStatusList?.revocationList).toEqual([ + 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, + ]) + }) + }) +}) diff --git a/packages/hedera/tests/integration/hedera-did-registrar.e2e.test.ts b/packages/hedera/tests/integration/hedera-did-registrar.e2e.test.ts new file mode 100644 index 0000000000..f6c8b7d2fb --- /dev/null +++ b/packages/hedera/tests/integration/hedera-did-registrar.e2e.test.ts @@ -0,0 +1,368 @@ +import { + Agent, + ConsoleLogger, + DidDocument, + DidDocumentKey, + DidDocumentService, + LogLevel, + VerificationMethod, +} from '@credo-ts/core' +import { HederaDidCreateOptions, HederaDidUpdateOptions } from '../../src/ledger/HederaLedgerService' +import { getMultibasePublicKey } from '../../src/ledger/utils' +import { getHederaAgent } from './utils' + +const validDid = 'did:hedera:testnet:44eesExqdsUvLZ35FpnBPErqRGRnYbzzyG3wgCCYxkmq_0.0.6226170' + +const validService = new DidDocumentService({ + id: '#service-1', + type: 'CustomType', + serviceEndpoint: ['https://rand.io'], +}) + +function getValidVerificationMethod(publicKeyMultibase?: string) { + return new VerificationMethod({ + id: '#key-1', + type: 'Ed25519VerificationKey2020', + controller: validDid, + publicKeyMultibase: publicKeyMultibase ?? 'z44eesExqdsUvLZ35FpnBPErqRGRnYbzzyG3wgCCYxkmq', + }) +} + +function getValidDidDocument(publicKeyMultibase?: string) { + return new DidDocument({ + id: validDid, + verificationMethod: [getValidVerificationMethod(publicKeyMultibase)], + service: [validService], + }) +} + +describe('Hedera DID registrar', () => { + const logger = new ConsoleLogger(LogLevel.error) + let agent: Agent + + beforeAll(async () => { + agent = getHederaAgent({ + logger, + label: 'alice', + }) + await agent.initialize() + }) + + afterAll(async () => { + await agent.shutdown() + }) + + it('should create a did:hedera did document', async () => { + const didResult = await agent.dids.create({ + method: 'hedera', + options: { network: 'testnet' }, + }) + + expect(didResult).toMatchObject({ + didState: { + state: 'finished', + didDocument: { + verificationMethod: [ + { + type: 'Ed25519VerificationKey2020', + publicKeyMultibase: expect.any(String), + }, + ], + }, + }, + }) + }) + + it('should create a did:hedera did document with document presets', async () => { + const { keyId, publicJwk } = await agent.kms.createKey({ + type: { + crv: 'Ed25519', + kty: 'OKP', + }, + }) + const multibasePublicKey = getMultibasePublicKey(publicJwk) + const keys: DidDocumentKey[] = [ + { + kmsKeyId: keyId, + didDocumentRelativeKeyId: '#key-1', + }, + ] + + const didResult = await agent.dids.create({ + method: 'hedera', + didDocument: getValidDidDocument(multibasePublicKey), + options: { network: 'testnet' }, + secret: { keys }, + }) + expect(didResult.didState.state).toEqual('finished') + + const verificationMethod = getValidVerificationMethod(multibasePublicKey) + expect(didResult.didState.didDocument?.verificationMethod).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.stringContaining('#did-root-key'), + type: expect.any(String), + controller: didResult.didState.didDocument?.id, + publicKeyMultibase: expect.any(String), + }), + expect.objectContaining({ + id: expect.stringContaining(verificationMethod.id), + type: verificationMethod.type, + controller: verificationMethod.controller, + publicKeyMultibase: verificationMethod.publicKeyMultibase, + }), + ]) + ) + + expect(didResult.didState.didDocument?.service).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.stringContaining(validService.id), + type: validService.type, + serviceEndpoint: validService.serviceEndpoint, + }), + ]) + ) + }) + + it('should create a did:hedera did document, add and remove service', async () => { + const didResult = await agent.dids.create({ + method: 'hedera', + options: { + network: 'testnet', + }, + }) + expect(didResult).toMatchObject({ didState: { state: 'finished' } }) + + const did = didResult.didState.did ?? '' + const didDocument = didResult.didState.didDocument as DidDocument + didDocument.service = [validService] + + const addUpdateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + + expect(addUpdateResult.didState.state).toEqual('finished') + expect(addUpdateResult.didState.didDocument?.id).toEqual(did) + + const resolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) + expect(resolvedDocument.didDocument?.id).toEqual(did) + + expect(resolvedDocument.didDocument?.service).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.stringContaining(validService.id), + type: validService.type, + serviceEndpoint: validService.serviceEndpoint, + }), + ]) + ) + + const removeUpdateResult = await agent.dids.update({ + did, + didDocument: { + ...didDocument, + verificationMethod: undefined, + }, + didDocumentOperation: 'removeFromDidDocument', + }) + + expect(removeUpdateResult.didState.state).toEqual('finished') + expect(removeUpdateResult.didState.didDocument?.id).toEqual(did) + + const removeResolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) + expect(removeResolvedDocument.didDocument?.id).toEqual(did) + expect(removeResolvedDocument.didDocument?.service ?? []).toHaveLength(0) + }) + + it('should create a did:hedera did document, add and remove verification method', async () => { + const { keyId, publicJwk } = await agent.kms.createKey({ + type: { + crv: 'Ed25519', + kty: 'OKP', + }, + }) + const multibasePublicKey = getMultibasePublicKey(publicJwk) + const keys: DidDocumentKey[] = [ + { + kmsKeyId: keyId, + didDocumentRelativeKeyId: '#key-1', + }, + ] + + const didResult = await agent.dids.create({ + method: 'hedera', + options: { network: 'testnet' }, + }) + expect(didResult).toMatchObject({ didState: { state: 'finished' } }) + + const did = didResult.didState.did ?? '' + const didDocument = didResult.didState.didDocument as DidDocument + + const validVerificationMethod = getValidVerificationMethod(multibasePublicKey) + didDocument.verificationMethod = [validVerificationMethod] + + const addUpdateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + secret: { + keys, + }, + }) + expect(addUpdateResult.didState.didDocument?.id).toEqual(did) + expect(addUpdateResult.didState.state).toEqual('finished') + + const addResolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) + + expect(addResolvedDocument.didDocument?.id).toEqual(did) + expect(addResolvedDocument.didDocument?.verificationMethod).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: expect.stringContaining(validVerificationMethod.id), + type: validVerificationMethod.type, + controller: validVerificationMethod.controller, + publicKeyMultibase: validVerificationMethod.publicKeyMultibase, + }), + ]) + ) + + const removeUpdateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'removeFromDidDocument', + secret: { + keys, + }, + }) + expect(removeUpdateResult.didState.didDocument?.id).toEqual(did) + expect(removeUpdateResult.didState.state).toEqual('finished') + + const removeResolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) + + expect(removeResolvedDocument.didDocument?.id).toEqual(did) + expect(removeResolvedDocument.didDocument?.service ?? []).toHaveLength(0) + }) + + it('should create a did:hedera did document, but should not add verification method without required keys', async () => { + const { publicJwk } = await agent.kms.createKey({ + type: { + crv: 'Ed25519', + kty: 'OKP', + }, + }) + const multibasePublicKey = getMultibasePublicKey(publicJwk) + + const didResult = await agent.dids.create({ + method: 'hedera', + options: { network: 'testnet' }, + }) + expect(didResult).toMatchObject({ didState: { state: 'finished' } }) + + const did = didResult.didState.did ?? '' + const didDocument = didResult.didState.didDocument as DidDocument + + const validVerificationMethod = getValidVerificationMethod(multibasePublicKey) + didDocument.verificationMethod = [validVerificationMethod] + + const expectedFailureReason = + 'Unable update DID: Key #key-1 is present in updated DID Document, but missing from DID record keys and DID update arguments' + + let updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + + didDocument.verificationMethod = undefined + didDocument.assertionMethod = [validVerificationMethod] + + updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + + didDocument.assertionMethod = undefined + didDocument.authentication = [validVerificationMethod] + + updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + + didDocument.authentication = undefined + didDocument.capabilityDelegation = [validVerificationMethod] + + updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + + didDocument.capabilityDelegation = undefined + didDocument.capabilityInvocation = [validVerificationMethod] + + updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + + didDocument.capabilityInvocation = undefined + didDocument.keyAgreement = [validVerificationMethod] + + updateResult = await agent.dids.update({ + did, + didDocument, + didDocumentOperation: 'addToDidDocument', + }) + expect(updateResult.didState.state).toEqual('failed') + if (updateResult.didState.state === 'failed') expect(updateResult.didState.reason).toEqual(expectedFailureReason) + }) + + it('should create and deactivate a did:hedera did', async () => { + const didResult = await agent.dids.create({ + method: 'hedera', + options: { + network: 'testnet', + }, + }) + expect(didResult).toMatchObject({ didState: { state: 'finished' } }) + + const did = didResult.didState.did ?? '' + + const deactivateResult = await agent.dids.deactivate({ + did, + }) + + expect(deactivateResult.didState.didDocument?.id).toEqual(did) + expect(deactivateResult.didState.state).toEqual('finished') + + const resolvedDocument = await agent.dids.resolve(did, { + useLocalCreatedDidRecord: false, + }) + expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true) + }) +}) diff --git a/packages/hedera/tests/integration/hedera-did-resolver.e2e.test.ts b/packages/hedera/tests/integration/hedera-did-resolver.e2e.test.ts new file mode 100644 index 0000000000..cac437d51a --- /dev/null +++ b/packages/hedera/tests/integration/hedera-did-resolver.e2e.test.ts @@ -0,0 +1,52 @@ +import { Agent, ConsoleLogger, JsonTransformer, LogLevel } from '@credo-ts/core' +import { HederaDidCreateOptions } from '../../src/ledger/HederaLedgerService' +import { getHederaAgent } from './utils' + +describe('Hedera DID resolver', () => { + const logger = new ConsoleLogger(LogLevel.error) + + let agent: Agent + let did: string + + beforeAll(async () => { + agent = getHederaAgent({ + logger, + label: 'alice', + }) + await agent.initialize() + + const didResult = await agent.dids.create({ + method: 'hedera', + }) + if (!didResult.didState.did) { + throw new Error('No DID created') + } + did = didResult.didState.did + }) + + afterAll(async () => { + await agent.shutdown() + }) + + it('should resolve a did:hedera did from testnet', async () => { + const resolveResult = await agent.dids.resolve(did) + + expect(JsonTransformer.toJSON(resolveResult)).toMatchObject({ + didDocument: { + '@context': ['https://www.w3.org/ns/did/v1'], + id: did, + controller: did, + verificationMethod: [ + { + controller: did, + id: `${did}#did-root-key`, + type: 'Ed25519VerificationKey2020', + publicKeyMultibase: expect.any(String), + }, + ], + }, + didDocumentMetadata: {}, + didResolutionMetadata: {}, + }) + }) +}) diff --git a/packages/hedera/tests/integration/utils/hederaModule.ts b/packages/hedera/tests/integration/utils/hederaModule.ts new file mode 100644 index 0000000000..833c6bb2ea --- /dev/null +++ b/packages/hedera/tests/integration/utils/hederaModule.ts @@ -0,0 +1,69 @@ +import { AnonCredsModule } from '@credo-ts/anoncreds' +import { AskarModule } from '@credo-ts/askar' +import { Agent, Cache, CacheModule, DidsModule, Logger, ModulesMap, utils } from '@credo-ts/core' +import { + HederaAnonCredsRegistry, + HederaDidRegistrar, + HederaDidResolver, + HederaModule, + HederaModuleConfigOptions, +} from '@credo-ts/hedera' +import { agentDependencies } from '@credo-ts/node' +import { HederaNetwork } from '@hiero-did-sdk/client' +import { anoncreds } from '@hyperledger/anoncreds-nodejs' +import { askar } from '@openwallet-foundation/askar-nodejs' +import { InMemoryTailsFileService } from '../../../../anoncreds/tests/InMemoryTailsFileService' + +export const getHederaModuleConfig = (props: { + network?: HederaNetwork + operatorId?: string + operatorKey?: string +}): HederaModuleConfigOptions => { + return { + networks: [ + { + network: props.network ?? (process.env.HEDERA_NETWORK as HederaNetwork) ?? 'testnet', + operatorId: props.operatorId ?? process.env.HEDERA_OPERATOR_ID ?? '0.0.5489553', + operatorKey: + props.operatorKey ?? + process.env.HEDERA_OPERATOR_KEY ?? + '302e020100300506032b6570042204209f54b75b6238ced43e41b1463999cb40bf2f7dd2c9fd4fd3ef780027c016a138', + }, + ], + } +} + +export const getHederaAgent = (props: { + operatorId?: string + operatorKey?: string + label?: string + logger?: Logger + cache?: Cache +}) => { + const label = props.label ?? utils.uuid() + const logger = props.logger + const cache = props.cache + + let modules: ModulesMap = { + askar: new AskarModule({ askar, store: { id: label, key: label } }), + anoncreds: new AnonCredsModule({ + anoncreds, + registries: [new HederaAnonCredsRegistry()], + tailsFileService: new InMemoryTailsFileService(), + }), + dids: new DidsModule({ + resolvers: [new HederaDidResolver()], + registrars: [new HederaDidRegistrar()], + }), + hedera: new HederaModule(getHederaModuleConfig(props)), + } + if (cache) { + modules = { ...modules, cache: new CacheModule({ cache }) } + } + + return new Agent({ + config: { label, logger }, + dependencies: agentDependencies, + modules, + }) +} diff --git a/packages/hedera/tests/integration/utils/index.ts b/packages/hedera/tests/integration/utils/index.ts new file mode 100644 index 0000000000..bb406f518b --- /dev/null +++ b/packages/hedera/tests/integration/utils/index.ts @@ -0,0 +1 @@ +export * from './hederaModule' diff --git a/packages/hedera/tests/setup.ts b/packages/hedera/tests/setup.ts new file mode 100644 index 0000000000..cc76304a17 --- /dev/null +++ b/packages/hedera/tests/setup.ts @@ -0,0 +1 @@ +jest.setTimeout(100000) diff --git a/packages/hedera/tests/unit/credo-cache.test.ts b/packages/hedera/tests/unit/credo-cache.test.ts new file mode 100644 index 0000000000..b9927933ad --- /dev/null +++ b/packages/hedera/tests/unit/credo-cache.test.ts @@ -0,0 +1,100 @@ +import { AgentContext, CacheModuleConfig, CredoError } from '@credo-ts/core' +import { Cache as CoreCredoCache } from '@credo-ts/core' +import { CredoCache } from '../../src/ledger/cache/CredoCache' + +describe('CredoCache', () => { + let mockAgentContext: AgentContext + let mockDependencyManagerResolve: jest.Mock + let mockCredoCache: jest.Mocked + + beforeEach(() => { + mockCredoCache = { + get: jest.fn(), + set: jest.fn(), + remove: jest.fn(), + } + + mockDependencyManagerResolve = jest.fn().mockReturnValue({ cache: mockCredoCache }) + + mockAgentContext = { + dependencyManager: { + resolve: mockDependencyManagerResolve, + }, + } as unknown as AgentContext + }) + + it('should throw CredoError if cache not found in constructor', () => { + mockDependencyManagerResolve.mockReturnValue({ cache: null }) + + expect(() => new CredoCache(mockAgentContext)).toThrowError(CredoError) + }) + + it('should initialize credoCache from dependency manager', () => { + const credoCacheInstance = new CredoCache(mockAgentContext) + expect(mockDependencyManagerResolve).toHaveBeenCalledWith(CacheModuleConfig) + // @ts-ignore + expect(credoCacheInstance.credoCache).toBe(mockCredoCache) + }) + + describe('get', () => { + it('should call credoCache.get with correct parameters and return value', async () => { + const testKey = 'test-key' + const returnedValue = { foo: 'bar' } + mockCredoCache.get.mockResolvedValue(returnedValue) + + const credoCacheInstance = new CredoCache(mockAgentContext) + const result = await credoCacheInstance.get(testKey) + + expect(mockCredoCache.get).toHaveBeenCalledWith(mockAgentContext, testKey) + expect(result).toBe(returnedValue) + }) + + it('should return null if credoCache.get resolves null', async () => { + mockCredoCache.get.mockResolvedValue(null) + + const credoCacheInstance = new CredoCache(mockAgentContext) + const result = await credoCacheInstance.get('missing') + + expect(result).toBeNull() + }) + }) + + describe('set', () => { + it('should call credoCache.set with correct parameters', async () => { + const key = 'key' + const value = { a: 1 } + mockCredoCache.set.mockResolvedValue(undefined) + + const credoCacheInstance = new CredoCache(mockAgentContext) + await credoCacheInstance.set(key, value, 123) + + expect(mockCredoCache.set).toHaveBeenCalledWith(mockAgentContext, key, value) + }) + }) + + describe('remove', () => { + it('should call credoCache.remove with correct parameters', async () => { + const key = 'keyToRemove' + mockCredoCache.remove.mockResolvedValue(undefined) + + const credoCacheInstance = new CredoCache(mockAgentContext) + await credoCacheInstance.remove(key) + + expect(mockCredoCache.remove).toHaveBeenCalledWith(mockAgentContext, key) + }) + }) + + describe('clear', () => { + it('should throw error when called', async () => { + const credoCacheInstance = new CredoCache(mockAgentContext) + await expect(credoCacheInstance.clear()).resolves.not.toThrow() + }) + }) + + describe('cleanupExpired', () => { + it('should throw error when called', async () => { + const credoCacheInstance = new CredoCache(mockAgentContext) + await expect(credoCacheInstance.cleanupExpired()).resolves.not.toThrow() + }) + }) +}) diff --git a/packages/hedera/tests/unit/fixtures/did-document.ts b/packages/hedera/tests/unit/fixtures/did-document.ts new file mode 100644 index 0000000000..723b1b2cad --- /dev/null +++ b/packages/hedera/tests/unit/fixtures/did-document.ts @@ -0,0 +1,28 @@ +import { ParsedDid, parseDid } from '@credo-ts/core' +import { DIDResolutionMetadata, JsonLdDIDDocument } from '@hiero-did-sdk/core' + +export const did = 'did:hedera:testnet:4BGybF4yCeYNi8RFVowK3zHc1xs2psYdkbiEvETrp3HL_0.0.1000001' +export const parsedDid: ParsedDid = parseDid(did) + +export const didDocument: Required> & JsonLdDIDDocument = { + '@context': [ + 'https://w3.org/ns/did/v1', + 'https://w3id.org/security/suites/ed25519-2018/v1', + 'https://w3id.org/security/suites/ed25519-2020/v1', + ], + id: did, + controller: did, + service: [{ id: 'mock-service', type: 'MockService', serviceEndpoint: 'https://example.com/mock-service/' }], + verificationMethod: [ + { + id: `${did}#did-root-key`, + controller: did, + type: 'Ed25519VerificationKey2020' as const, + publicKeyMultibase: 'z6MkhdY2BVKQYC2qpdFxBNu9u5qbqY8tEknzScdAkWRsjG4i', + }, + ], +} + +export const didResolutionMetadata: DIDResolutionMetadata = { + contentType: 'application/ld+json;profile="https://w3id.org/did-resolution"', +} diff --git a/packages/hedera/tests/unit/hedera-anoncres-registry.test.ts b/packages/hedera/tests/unit/hedera-anoncres-registry.test.ts new file mode 100644 index 0000000000..0b2344449a --- /dev/null +++ b/packages/hedera/tests/unit/hedera-anoncres-registry.test.ts @@ -0,0 +1,439 @@ +import { + GetCredentialDefinitionReturn, + GetRevocationRegistryDefinitionReturn, + GetRevocationStatusListReturn, + GetSchemaReturn, + RegisterCredentialDefinitionOptions, + RegisterCredentialDefinitionReturn, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationRegistryDefinitionReturn, + RegisterRevocationStatusListOptions, + RegisterRevocationStatusListReturn, + RegisterSchemaOptions, + RegisterSchemaReturn, +} from '@credo-ts/anoncreds' +import { AgentContext } from '@credo-ts/core' +import { HederaAnonCredsRegistry } from '@credo-ts/hedera' +import { mockFunction } from '../../../core/tests/helpers' +import { HederaLedgerService } from '../../src/ledger/HederaLedgerService' + +const mockLedgerService = { + registerSchema: jest.fn(), + getSchema: jest.fn(), + registerCredentialDefinition: jest.fn(), + getCredentialDefinition: jest.fn(), + registerRevocationRegistryDefinition: jest.fn(), + getRevocationRegistryDefinition: jest.fn(), + registerRevocationStatusList: jest.fn(), + getRevocationStatusList: jest.fn(), +} as unknown as HederaLedgerService + +const mockAgentContext = { + dependencyManager: { + resolve: jest.fn().mockImplementation((cls) => { + if (cls === HederaLedgerService) return mockLedgerService + }), + }, + config: { + logger: { + trace: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + }, + }, +} as unknown as AgentContext + +describe('HederaAnonCredsRegistry', () => { + const registry: HederaAnonCredsRegistry = new HederaAnonCredsRegistry() + + describe('registerSchema', () => { + const options: RegisterSchemaOptions = { + schema: { + issuerId: 'issuer-did', + name: 'schema-name', + version: '1.0', + attrNames: [], + }, + options: {}, + } + + it('should call ledgerService.registerSchema and return result on success', async () => { + const expected: RegisterSchemaReturn = { + schemaMetadata: {}, + registrationMetadata: {}, + schemaState: { + state: 'finished', + schema: options.schema, + schemaId: expect.any(String), + }, + } + mockFunction(mockLedgerService.registerSchema).mockResolvedValue(expected) + + const result = await registry.registerSchema(mockAgentContext, options) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith('Registering schema on Hedera ledger') + expect(mockAgentContext.dependencyManager.resolve).toHaveBeenCalledWith( + expect.any(Function) || HederaLedgerService + ) + expect(mockLedgerService.registerSchema).toHaveBeenCalledWith(mockAgentContext, options) + expect(result).toEqual(expected) + }) + + it('should catch error and return failed state', async () => { + const error = new Error('fail') + mockFunction(mockLedgerService.registerSchema).mockRejectedValue(error) + + const result = await registry.registerSchema(mockAgentContext, options) + + expect(mockAgentContext.config.logger.debug).toHaveBeenCalledWith( + `Error registering schema for did '${options.schema.issuerId}'`, + expect.objectContaining({ error, did: options.schema.issuerId, schema: options }) + ) + expect(result.schemaState.state).toBe('failed') + if (result.schemaState.state === 'failed') expect(result.schemaState.reason).toContain('fail') + }) + }) + + describe('getSchema', () => { + const mockSchemaId = 'mock-schema-id' + + it('should call ledgerService.getSchema and return result on success', async () => { + const expected: GetSchemaReturn = { + schemaId: mockSchemaId, + resolutionMetadata: {}, + schemaMetadata: {}, + } + mockFunction(mockLedgerService.getSchema).mockResolvedValue(expected) + + const result = await registry.getSchema(mockAgentContext, mockSchemaId) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Resolving schema '${mockSchemaId}' from Hedera ledger` + ) + expect(mockLedgerService.getSchema).toHaveBeenCalledWith(mockAgentContext, mockSchemaId) + expect(result).toEqual(expected) + }) + + it('should catch error and return notFound error state', async () => { + const error = new Error('not found') + mockFunction(mockLedgerService.getSchema).mockRejectedValue(error) + + const result = await registry.getSchema(mockAgentContext, mockSchemaId) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error retrieving schema '${mockSchemaId}'`, + expect.objectContaining({ error, schemaId: mockSchemaId }) + ) + expect(result.resolutionMetadata.error).toBe('notFound') + expect(result.resolutionMetadata.message).toContain('not found') + }) + }) + + describe('registerCredentialDefinition', () => { + const options: RegisterCredentialDefinitionOptions = { + options: {}, + credentialDefinition: { + issuerId: 'did:hedera:issuer', + schemaId: '', + type: 'CL', + tag: '', + value: { + primary: {}, + revocation: undefined, + }, + }, + } + + it('should call ledgerService.registerCredentialDefinition and return result on success', async () => { + const expected: RegisterCredentialDefinitionReturn = { + credentialDefinitionMetadata: {}, + registrationMetadata: {}, + credentialDefinitionState: { + state: 'finished', + credentialDefinition: { + issuerId: '', + schemaId: '', + type: 'CL', + tag: '', + value: { + primary: {}, + revocation: undefined, + }, + }, + credentialDefinitionId: 'did:hedera:issuerId', + }, + } + mockFunction(mockLedgerService.registerCredentialDefinition).mockResolvedValue(expected) + + const result = await registry.registerCredentialDefinition(mockAgentContext, options) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + 'Registering credential definition on Hedera ledger' + ) + expect(mockLedgerService.registerCredentialDefinition).toHaveBeenCalledWith(mockAgentContext, options) + expect(result).toEqual(expected) + }) + + it('should catch error and return failed state', async () => { + const error = new Error('fail') + mockFunction(mockLedgerService.registerCredentialDefinition).mockRejectedValue(error) + + const result = await registry.registerCredentialDefinition(mockAgentContext, options) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error registering credential definition for did '${options.credentialDefinition.issuerId}'`, + expect.objectContaining({ error, did: options.credentialDefinition.issuerId, schema: options }) + ) + expect(result.credentialDefinitionState.state).toBe('failed') + if (result.credentialDefinitionState.state === 'failed') + expect(result.credentialDefinitionState.reason).toContain('fail') + }) + }) + + describe('getCredentialDefinition', () => { + const mockCredentialDefinitionId = 'mock-cred-def-id' + + it('should call ledgerService.getCredentialDefinition and return result on success', async () => { + const expected: GetCredentialDefinitionReturn = { + credentialDefinitionId: mockCredentialDefinitionId, + resolutionMetadata: {}, + credentialDefinitionMetadata: {}, + } + mockFunction(mockLedgerService.getCredentialDefinition).mockResolvedValue(expected) + + const result = await registry.getCredentialDefinition(mockAgentContext, mockCredentialDefinitionId) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Resolving credential definition '${mockCredentialDefinitionId}' from Hedera ledger` + ) + expect(mockLedgerService.getCredentialDefinition).toHaveBeenCalledWith( + mockAgentContext, + mockCredentialDefinitionId + ) + expect(result).toEqual(expected) + }) + + it('should catch error and return notFound error state', async () => { + const error = new Error('not found') + mockFunction(mockLedgerService.getCredentialDefinition).mockRejectedValue(error) + + const result = await registry.getCredentialDefinition(mockAgentContext, mockCredentialDefinitionId) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error retrieving credential definition '${mockCredentialDefinitionId}'`, + expect.objectContaining({ error, credentialDefinitionId: mockCredentialDefinitionId }) + ) + expect(result.resolutionMetadata.error).toBe('notFound') + expect(result.resolutionMetadata.message).toContain('not found') + }) + }) + + describe('registerRevocationRegistryDefinition', () => { + const options: RegisterRevocationRegistryDefinitionOptions = { + options: {}, + revocationRegistryDefinition: { + credDefId: 'credDef1', + issuerId: 'did:hedera:issuer', + revocDefType: 'CL_ACCUM', + tag: '', + value: { + publicKeys: { + accumKey: { + z: '', + }, + }, + maxCredNum: 0, + tailsLocation: '', + tailsHash: '', + }, + }, + } + + it('should call ledgerService.registerRevocationRegistryDefinition and return result on success', async () => { + const expected: RegisterRevocationRegistryDefinitionReturn = { + revocationRegistryDefinitionMetadata: {}, + registrationMetadata: {}, + revocationRegistryDefinitionState: { + state: 'finished', + revocationRegistryDefinitionId: 'test', + revocationRegistryDefinition: { + issuerId: '', + revocDefType: 'CL_ACCUM', + credDefId: '', + tag: '', + value: { + publicKeys: { + accumKey: { + z: '', + }, + }, + maxCredNum: 0, + tailsLocation: '', + tailsHash: '', + }, + }, + }, + } + mockFunction(mockLedgerService.registerRevocationRegistryDefinition).mockResolvedValue(expected) + + const result = await registry.registerRevocationRegistryDefinition(mockAgentContext, options) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Registering revocation registry definition for '${options.revocationRegistryDefinition.credDefId}' on Hedera ledger` + ) + expect(mockLedgerService.registerRevocationRegistryDefinition).toHaveBeenCalledWith(mockAgentContext, options) + expect(result).toEqual(expected) + }) + + it('should catch error and return failed state', async () => { + const error = new Error('fail') + mockFunction(mockLedgerService.registerRevocationRegistryDefinition).mockRejectedValue(error) + + const result = await registry.registerRevocationRegistryDefinition(mockAgentContext, options) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error registering revocation registry definition for did '${options.revocationRegistryDefinition.issuerId}'`, + expect.objectContaining({ error, did: options.revocationRegistryDefinition.issuerId, options }) + ) + expect(result.revocationRegistryDefinitionState.state).toBe('failed') + if (result.revocationRegistryDefinitionState.state === 'failed') + expect(result.revocationRegistryDefinitionState.reason).toContain('fail') + }) + }) + + describe('getRevocationRegistryDefinition', () => { + const mockRevocationRegistryDefinitionId = 'mock-rev-reg-def-id' + + it('should call ledgerService.getRevocationRegistryDefinition and return result on success', async () => { + const expected: GetRevocationRegistryDefinitionReturn = { + revocationRegistryDefinitionId: mockRevocationRegistryDefinitionId, + resolutionMetadata: {}, + revocationRegistryDefinitionMetadata: {}, + } + mockFunction(mockLedgerService.getRevocationRegistryDefinition).mockResolvedValue(expected) + + const result = await registry.getRevocationRegistryDefinition( + mockAgentContext, + mockRevocationRegistryDefinitionId + ) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Resolving revocation registry definition for '${mockRevocationRegistryDefinitionId}' from Hedera ledger` + ) + expect(mockLedgerService.getRevocationRegistryDefinition).toHaveBeenCalledWith( + mockAgentContext, + mockRevocationRegistryDefinitionId + ) + expect(result).toEqual(expected) + }) + + it('should catch error and return notFound error state', async () => { + const error = new Error('not found') + mockFunction(mockLedgerService.getRevocationRegistryDefinition).mockRejectedValue(error) + + const result = await registry.getRevocationRegistryDefinition( + mockAgentContext, + mockRevocationRegistryDefinitionId + ) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error retrieving revocation registry definition '${mockRevocationRegistryDefinitionId}'`, + expect.objectContaining({ error, revocationRegistryDefinitionId: mockRevocationRegistryDefinitionId }) + ) + expect(result.resolutionMetadata.error).toBe('notFound') + expect(result.resolutionMetadata.message).toContain('not found') + }) + }) + + describe('registerRevocationStatusList', () => { + const options: RegisterRevocationStatusListOptions = { + options: {}, + revocationStatusList: { + revRegDefId: 'regDef1', + issuerId: 'did:hedera:issuer', + revocationList: [], + currentAccumulator: '', + }, + } + + it('should call ledgerService.registerRevocationStatusList and return result on success', async () => { + const expected: RegisterRevocationStatusListReturn = { + revocationStatusListMetadata: {}, + registrationMetadata: {}, + revocationStatusListState: { + state: 'finished', + revocationStatusList: { + revRegDefId: '', + issuerId: '', + revocationList: [], + timestamp: 0, + currentAccumulator: '', + }, + }, + } + mockFunction(mockLedgerService.registerRevocationStatusList).mockResolvedValue(expected) + + const result = await registry.registerRevocationStatusList(mockAgentContext, options) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Registering revocation status list for '${options.revocationStatusList.revRegDefId}' on Hedera ledger` + ) + expect(mockLedgerService.registerRevocationStatusList).toHaveBeenCalledWith(mockAgentContext, options) + expect(result).toEqual(expected) + }) + + it('should catch error and return failed state', async () => { + const error = new Error('fail') + mockFunction(mockLedgerService.registerRevocationStatusList).mockRejectedValue(error) + + const result = await registry.registerRevocationStatusList(mockAgentContext, options) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error registering revocation status list for did '${options.revocationStatusList.issuerId}'`, + expect.objectContaining({ error, did: options.revocationStatusList.issuerId, options }) + ) + expect(result.revocationStatusListState.state).toBe('failed') + if (result.revocationStatusListState.state === 'failed') + expect(result.revocationStatusListState.reason).toContain('fail') + }) + }) + + describe('getRevocationStatusList', () => { + const mockRevocationRegistryId = 'mock-rev-reg-def-id' + const timestamp = 1234567890 + + it('should call ledgerService.getRevocationStatusList and return result on success', async () => { + const expected: GetRevocationStatusListReturn = { + resolutionMetadata: {}, + revocationStatusListMetadata: {}, + } + mockFunction(mockLedgerService.getRevocationStatusList).mockResolvedValue(expected) + + const result = await registry.getRevocationStatusList(mockAgentContext, mockRevocationRegistryId, timestamp) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith( + `Resolving revocation status for for '${mockRevocationRegistryId}' from Hedera ledger` + ) + expect(mockLedgerService.getRevocationStatusList).toHaveBeenCalledWith( + mockAgentContext, + mockRevocationRegistryId, + timestamp * 1000 + ) + expect(result).toEqual(expected) + }) + + it('should catch error and return notFound error state', async () => { + const error = new Error('not found') + mockFunction(mockLedgerService.getRevocationStatusList).mockRejectedValue(error) + + const result = await registry.getRevocationStatusList(mockAgentContext, mockRevocationRegistryId, timestamp) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith( + `Error retrieving revocation registry status list '${mockRevocationRegistryId}'`, + expect.objectContaining({ error, revocationRegistryId: mockRevocationRegistryId }) + ) + expect(result.resolutionMetadata.error).toBe('notFound') + expect(result.resolutionMetadata.message).toContain('not found') + }) + }) +}) diff --git a/packages/hedera/tests/unit/hedera-did-registrar.test.ts b/packages/hedera/tests/unit/hedera-did-registrar.test.ts new file mode 100644 index 0000000000..02d6cd3b58 --- /dev/null +++ b/packages/hedera/tests/unit/hedera-did-registrar.test.ts @@ -0,0 +1,266 @@ +import { AgentContext, DidDocumentKey, DidRecord, DidRepository } from '@credo-ts/core' + +import { DidDocumentRole } from '@credo-ts/core' +import { HederaDidRegistrar } from '@credo-ts/hedera' +import { mockFunction } from '../../../core/tests/helpers' +import { HederaDidUpdateOptions, HederaLedgerService } from '../../src/ledger/HederaLedgerService' +import { did, didDocument, didResolutionMetadata } from './fixtures/did-document' + +const mockDidRepository = { + save: jest.fn(), + findCreatedDid: jest.fn(), + update: jest.fn(), +} as unknown as DidRepository + +const mockLedgerService = { + createDid: jest.fn(), + resolveDid: jest.fn(), + updateDid: jest.fn(), + deactivateDid: jest.fn(), +} as unknown as HederaLedgerService + +const mockAgentContext = { + dependencyManager: { + resolve: jest.fn().mockImplementation((cls) => { + if (cls === DidRepository) return mockDidRepository + if (cls === HederaLedgerService) return mockLedgerService + }), + }, + config: { + logger: { + debug: jest.fn(), + error: jest.fn(), + }, + }, +} as unknown as AgentContext + +describe('HederaDidRegistrar', () => { + const registrar: HederaDidRegistrar = new HederaDidRegistrar() + + describe('create', () => { + it('should create DID, save it, and return finished state on success', async () => { + const rootKey = { kmsKeyId: 'key1', didDocumentRelativeKeyId: 'rootKeyId' } + + mockFunction(mockLedgerService.createDid).mockResolvedValue({ + did, + didDocument, + rootKey, + }) + + const result = await registrar.create(mockAgentContext, { + method: 'hedera', + options: {}, + }) + + expect(mockDidRepository.save).toHaveBeenCalled() + const savedRecord = mockFunction(mockDidRepository.save).mock.calls[0][1] + expect(savedRecord.did).toBe(did) + expect(savedRecord.role).toBe(DidDocumentRole.Created) + expect(savedRecord.didDocument).toBeInstanceOf(Object) + expect(savedRecord.didDocument?.service).toBeDefined() + // biome-ignore lint/style/noNonNullAssertion: + expect(savedRecord.didDocument?.service![0]).toBeInstanceOf(Object) + + expect(result.didState.state).toBe('finished') + expect(result.didState.did).toBe(did) + expect(result.didState.didDocument).toBeInstanceOf(Object) + }) + + it('should handle error and return failed state', async () => { + mockFunction(mockLedgerService.createDid).mockRejectedValue(new Error('Create failed')) + + const result = await registrar.create(mockAgentContext, { + method: 'hedera', + options: {}, + }) + + expect(mockAgentContext.config.logger.debug).toHaveBeenCalledWith('Error creating DID', expect.any(Object)) + + expect(result.didState.state).toBe('failed') + if (result.didState.state === 'failed') + expect(result.didState.reason).toBe('Unable to register Did: Create failed') + }) + }) + + describe('update', () => { + it('should update DID and save record successfully', async () => { + const updatedDidDocument = { + ...didDocument, + service: [ + ...didDocument.service, + { id: 'added-service', type: 'MockService', serviceEndpoint: 'https://example.com/added-service/' }, + ], + } + + const foundDidRecord = { + didDocument, + keys: [{ didDocumentRelativeKeyId: 'key1' }], + } as unknown as DidRecord + + mockFunction(mockLedgerService.resolveDid).mockResolvedValue({ + didDocument, + didDocumentMetadata: { deactivated: false }, + didResolutionMetadata, + }) + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(foundDidRecord) + mockFunction(mockLedgerService.updateDid).mockResolvedValue({ did, didDocument: updatedDidDocument }) + + const options: HederaDidUpdateOptions = { + did, + didDocumentOperation: 'setDidDocument', + secret: { + keys: [ + { + didDocumentRelativeKeyId: 'key2', + kmsKeyId: 'some-key', + }, + ], + }, + didDocument: {}, + } + + const result = await registrar.update(mockAgentContext, options) + + expect(mockLedgerService.resolveDid).toHaveBeenCalledWith(mockAgentContext, did) + expect(mockDidRepository.findCreatedDid).toHaveBeenCalledWith(mockAgentContext, did) + + expect(mockLedgerService.updateDid).toHaveBeenCalledWith( + mockAgentContext, + expect.objectContaining({ + secret: { keys: expect.any(Array) }, + }) + ) + + expect(mockDidRepository.update).toHaveBeenCalledWith(mockAgentContext, foundDidRecord) + + expect(result.didState.state).toBe('finished') + expect(result.didState.did).toBe(did) + expect(result.didState.didDocument).toBeInstanceOf(Object) + }) + + it('should return failed state if DID not found or deactivated', async () => { + mockFunction(mockLedgerService.resolveDid).mockResolvedValue({ + didDocument, + didResolutionMetadata, + didDocumentMetadata: { deactivated: true }, + }) + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(null) + + const result = await registrar.update(mockAgentContext, { + did, + didDocument: {}, + }) + + expect(result.didState.state).toBe('failed') + if (result.didState.state === 'failed') expect(result.didState.reason).toBe('Did not found') + }) + + it('should handle error and return failed state', async () => { + mockFunction(mockLedgerService.resolveDid).mockRejectedValue(new Error('Update failed')) + + const result = await registrar.update(mockAgentContext, { + did, + didDocumentOperation: 'setDidDocument', + didDocument: {}, + }) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith('Error updating DID', expect.any(Error)) + expect(result.didState.state).toBe('failed') + if (result.didState.state === 'failed') expect(result.didState.reason).toBe('Unable update DID: Update failed') + }) + }) + + describe('deactivate', () => { + it('should deactivate DID and save updated record successfully', async () => { + const deactivatedDidDocument = { ...didDocument, deactivated: true } + + const foundDidRecord = { + didDocument, + keys: [{ didDocumentRelativeKeyId: 'key1' }], + } as unknown as DidRecord + + mockFunction(mockLedgerService.resolveDid).mockResolvedValue({ + didDocument, + didResolutionMetadata, + didDocumentMetadata: {}, + }) + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(foundDidRecord) + mockFunction(mockLedgerService.deactivateDid).mockResolvedValue({ + did, + didDocument: deactivatedDidDocument, + }) + mockFunction(mockDidRepository.update).mockResolvedValue(undefined) + + const result = await registrar.deactivate(mockAgentContext, { + did, + }) + + expect(mockLedgerService.resolveDid).toHaveBeenCalledWith(mockAgentContext, did) + expect(mockDidRepository.findCreatedDid).toHaveBeenCalledWith(mockAgentContext, did) + expect(mockLedgerService.deactivateDid).toHaveBeenCalledWith( + mockAgentContext, + expect.objectContaining({ + secret: { keys: foundDidRecord.keys }, + }) + ) + expect(mockDidRepository.update).toHaveBeenCalledWith(mockAgentContext, foundDidRecord) + + expect(result.didState.state).toBe('finished') + expect(result.didState.did).toBe(did) + expect(result.didState.didDocument).toBeInstanceOf(Object) + }) + + it('should return failed state if DID is deactivated', async () => { + mockFunction(mockLedgerService.resolveDid).mockResolvedValueOnce({ + didDocument, + didResolutionMetadata, + didDocumentMetadata: { deactivated: true }, + }) + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(null) + + const result = await registrar.deactivate(mockAgentContext, { + did, + }) + + expect(result.didState.state).toBe('failed') + // @ts-ignore + expect(result.didState.reason).toBe('Did not found') + }) + + it('should handle error and return failed state', async () => { + mockFunction(mockLedgerService.resolveDid).mockRejectedValue(new Error('Deactivate failed')) + + const result = await registrar.deactivate(mockAgentContext, { did }) + + expect(mockAgentContext.config.logger.error).toHaveBeenCalledWith('Error deactivating DID', expect.any(Error)) + expect(result.didState.state).toBe('failed') + if (result.didState.state === 'failed') + expect(result.didState.reason).toBe('Unable deactivating DID: Deactivate failed') + }) + }) + + describe('concatKeys (private method)', () => { + it('should concatenate keys without duplicates based on relativeKeyId', () => { + const keys1 = [{ didDocumentRelativeKeyId: 'key1' }, { didDocumentRelativeKeyId: 'key2' }] as DidDocumentKey[] + const keys2 = [{ didDocumentRelativeKeyId: 'key2' }, { didDocumentRelativeKeyId: 'key3' }] as DidDocumentKey[] + + // biome-ignore lint/suspicious/noExplicitAny: + const result = (registrar as any).concatKeys(keys1, keys2) + + expect(result).toHaveLength(3) + expect(result).toEqual( + expect.arrayContaining([ + { didDocumentRelativeKeyId: 'key1' }, + { didDocumentRelativeKeyId: 'key2' }, + { didDocumentRelativeKeyId: 'key3' }, + ]) + ) + }) + + it('should handle undefined arguments and return empty array', () => { + // biome-ignore lint/suspicious/noExplicitAny: + const result = (registrar as any).concatKeys(undefined, undefined) + expect(result).toEqual([]) + }) + }) +}) diff --git a/packages/hedera/tests/unit/hedera-did-resolver.test.ts b/packages/hedera/tests/unit/hedera-did-resolver.test.ts new file mode 100644 index 0000000000..f39904bcce --- /dev/null +++ b/packages/hedera/tests/unit/hedera-did-resolver.test.ts @@ -0,0 +1,70 @@ +import { AgentContext, DidDocument, JsonTransformer } from '@credo-ts/core' +import { HederaDidResolver } from '@credo-ts/hedera' +import { mockFunction } from '../../../core/tests/helpers' +import { HederaLedgerService } from '../../src/ledger/HederaLedgerService' +import { did, didDocument, didResolutionMetadata, parsedDid } from './fixtures/did-document' + +const mockLedgerService = { + resolveDid: jest.fn(), +} as unknown as HederaLedgerService + +const mockAgentContext = { + config: { + logger: { + trace: jest.fn(), + debug: jest.fn(), + }, + }, + dependencyManager: { + resolve: jest.fn().mockImplementation((cls) => { + if (cls === HederaLedgerService) return mockLedgerService + }), + }, +} as unknown as AgentContext + +const resolver = new HederaDidResolver() + +describe('HederaDidResolver', () => { + it('should successfully resolve DID', async () => { + const resolutionResult = { + didDocument, + didDocumentMetadata: {}, + didResolutionMetadata, + } + + mockFunction(mockLedgerService.resolveDid).mockResolvedValue(resolutionResult) + + jest.spyOn(JsonTransformer, 'fromJSON').mockReturnValue(didDocument) + + const result = await resolver.resolve(mockAgentContext, did, parsedDid, {}) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith('Try to resolve a did document from ledger') + expect(mockAgentContext.dependencyManager.resolve).toHaveBeenCalledWith(HederaLedgerService) + expect(mockLedgerService.resolveDid).toHaveBeenCalledWith(mockAgentContext, did) + expect(JsonTransformer.fromJSON).toHaveBeenCalledWith(resolutionResult.didDocument, DidDocument) + expect(result).toEqual(resolutionResult) + }) + + it('should handle error and return notFound', async () => { + const error = new Error('Some error') + + mockFunction(mockLedgerService.resolveDid).mockRejectedValue(error) + + const result = await resolver.resolve(mockAgentContext, did, parsedDid, {}) + + expect(mockAgentContext.config.logger.trace).toHaveBeenCalledWith('Try to resolve a did document from ledger') + expect(mockAgentContext.config.logger.debug).toHaveBeenCalledWith('Error resolving the did', { + error, + did, + }) + + expect(result).toEqual({ + didDocument: null, + didDocumentMetadata: {}, + didResolutionMetadata: { + error: 'notFound', + message: `Unable to resolve did '${did}': ${error}`, + }, + }) + }) +}) diff --git a/packages/hedera/tests/unit/hedera-ledger-service.test.ts b/packages/hedera/tests/unit/hedera-ledger-service.test.ts new file mode 100644 index 0000000000..6120d6355b --- /dev/null +++ b/packages/hedera/tests/unit/hedera-ledger-service.test.ts @@ -0,0 +1,566 @@ +import { + RegisterCredentialDefinitionOptions, + RegisterRevocationRegistryDefinitionOptions, + RegisterRevocationStatusListOptions, + RegisterSchemaOptions, +} from '@credo-ts/anoncreds' +import { DidDocument, DidRecord, DidRepository } from '@credo-ts/core' +import { AgentContext } from '@credo-ts/core' +import { DidDocumentKey, Kms } from '@credo-ts/core' +import { KmsJwkPublicOkp } from '@credo-ts/core/src/modules/kms' +import { Client, PrivateKey } from '@hashgraph/sdk' +import { HederaLedgerService } from '../../src/ledger/HederaLedgerService' + +jest.mock('@hiero-did-sdk/registrar', () => ({ + DIDUpdateBuilder: jest.fn().mockReturnValue({ + addService: jest.fn().mockReturnThis(), + removeService: jest.fn().mockReturnThis(), + addVerificationMethod: jest.fn().mockReturnThis(), + removeVerificationMethod: jest.fn().mockReturnThis(), + addAssertionMethod: jest.fn().mockReturnThis(), + removeAssertionMethod: jest.fn().mockReturnThis(), + addAuthenticationMethod: jest.fn().mockReturnThis(), + removeAuthenticationMethod: jest.fn().mockReturnThis(), + addCapabilityDelegationMethod: jest.fn().mockReturnThis(), + removeCapabilityDelegationMethod: jest.fn().mockReturnThis(), + addCapabilityInvocationMethod: jest.fn().mockReturnThis(), + removeCapabilityInvocationMethod: jest.fn().mockReturnThis(), + addKeyAgreementMethod: jest.fn().mockReturnThis(), + removeKeyAgreementMethod: jest.fn().mockReturnThis(), + build: jest.fn(), + }), + generateCreateDIDRequest: jest.fn(), + submitCreateDIDRequest: jest.fn(), + generateUpdateDIDRequest: jest.fn(), + submitUpdateDIDRequest: jest.fn(), + generateDeactivateDIDRequest: jest.fn(), + submitDeactivateDIDRequest: jest.fn(), +})) + +import { + CreateDIDRequest, + DIDUpdateBuilder, + DeactivateDIDRequest, + UpdateDIDRequest, + generateCreateDIDRequest, + generateDeactivateDIDRequest, + generateUpdateDIDRequest, + submitCreateDIDRequest, + submitDeactivateDIDRequest, + submitUpdateDIDRequest, +} from '@hiero-did-sdk/registrar' + +jest.mock('@hiero-did-sdk/resolver', () => ({ + resolveDID: jest.fn(), + TopicReaderHederaHcs: jest.fn(), +})) + +import { resolveDID } from '@hiero-did-sdk/resolver' + +jest.mock('../../src/ledger/utils') + +import { AskarKeyManagementService } from '@credo-ts/askar' +import { DID_ROOT_KEY_ID } from '@hiero-did-sdk/core' +import { KeyEntryObject } from '@openwallet-foundation/askar-nodejs' +import { mockFunction } from '../../../core/tests/helpers' +import { HederaAnonCredsRegistry } from '../../src/anoncreds/HederaAnonCredsRegistry' +import { createOrGetKey } from '../../src/ledger/utils' +import { did, didDocument } from './fixtures/did-document' + +const mockKeyId = 'mock-key-id' + +const mockPublicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } = { crv: 'Ed25519', kty: 'OKP', x: 'abc' } + +const mockKeyManagementService = { + getKeyAsserted: jest + .fn() + .mockReturnValue({ key: { secretBytes: PrivateKey.generateED25519().toBytes() } } as unknown as KeyEntryObject), +} as unknown as AskarKeyManagementService + +const mockKms = { + sign: jest.fn().mockResolvedValue({ signature: new Uint8Array([1, 2, 3]) }), + getKms: jest.fn().mockReturnValue(mockKeyManagementService), +} as unknown as Kms.KeyManagementApi + +const mockDidRepository = { + findCreatedDid: jest.fn().mockResolvedValue({ + keys: [ + { + didDocumentRelativeKeyId: DID_ROOT_KEY_ID, + kmsKeyId: 'kmsKeyId', + }, + ], + }), +} as unknown as DidRepository + +const mockAgentContext = { + dependencyManager: { + resolve: jest.fn((cls) => { + if (cls === Kms.KeyManagementApi) { + return mockKms + } + if (cls === DidRepository) { + return mockDidRepository + } + throw new Error(`No instance found for ${cls}`) + }), + }, +} as unknown as AgentContext + +const mockHederaAnonCredsRegistry = { + getSchema: jest.fn().mockResolvedValue('schema'), + registerSchema: jest.fn().mockResolvedValue('registerSchema'), + getCredentialDefinition: jest.fn().mockResolvedValue('credDef'), + registerCredentialDefinition: jest.fn().mockResolvedValue('registerCredDef'), + getRevocationRegistryDefinition: jest.fn().mockResolvedValue('revRegDef'), + registerRevocationRegistryDefinition: jest.fn().mockResolvedValue('registerRevRegDef'), + getRevocationStatusList: jest.fn().mockResolvedValue('revStatusList'), + registerRevocationStatusList: jest.fn().mockResolvedValue('registerRevStatus'), +} as unknown as HederaAnonCredsRegistry + +describe('HederaLedgerService', () => { + const service = new HederaLedgerService({ + options: { + networks: [ + { + network: 'testnet', + operatorId: 'mock-operator-id', + operatorKey: 'mock-operator-key', + }, + ], + cache: { + get: jest.fn().mockResolvedValue(null), + set: jest.fn().mockResolvedValue(undefined), + remove: jest.fn().mockResolvedValue(undefined), + clear: jest.fn(), + cleanupExpired: jest.fn(), + }, + }, + }) + const builder: DIDUpdateBuilder = new DIDUpdateBuilder() + + // biome-ignore lint/suspicious/noExplicitAny: + jest.spyOn((service as any).clientService, 'withClient').mockImplementation(async (_props, operation) => { + const mockClient = {} as Client + // @ts-ignore + return operation(mockClient) + }) + + // biome-ignore lint/suspicious/noExplicitAny: + jest.spyOn(service as any, 'getHederaAnonCredsRegistry').mockReturnValue(mockHederaAnonCredsRegistry) + + // biome-ignore lint/suspicious/noExplicitAny: + jest.spyOn(service as any, 'getPublisher').mockResolvedValue({}) + + describe('resolveDid', () => + it('should call resolveDID with proper args and returns result', async () => { + // @ts-ignore - there is a conflict with 'resolveDID' "overloaded" signatures + mockFunction(resolveDID).mockResolvedValueOnce(didDocument) + + const result = await service.resolveDid(mockAgentContext, did) + + expect(resolveDID).toHaveBeenCalledWith( + did, + 'application/ld+json;profile="https://w3id.org/did-resolution"', + expect.any(Object) + ) + expect(result).toBe(didDocument) + })) + + describe('createDid', () => { + it('should create DID without didDocument', async () => { + const createDidRequest = { + state: {}, + signingRequest: { serializedPayload: new Uint8Array() }, + } as unknown as CreateDIDRequest + + mockFunction(createOrGetKey).mockResolvedValueOnce({ keyId: mockKeyId, publicJwk: mockPublicJwk }) + mockFunction(generateCreateDIDRequest).mockResolvedValueOnce(createDidRequest) + mockFunction(submitCreateDIDRequest).mockResolvedValueOnce({ did, didDocument }) + + const result = await service.createDid(mockAgentContext, { + method: 'hedera', + options: { network: 'testnet' }, + secret: { rootKeyId: mockKeyId, keys: [] }, + }) + + expect(createOrGetKey).toHaveBeenCalledWith(mockKms, mockKeyId) + expect(generateCreateDIDRequest).toHaveBeenCalled() + expect(submitCreateDIDRequest).toHaveBeenCalled() + expect(result.did).toBe(did) + expect(result.rootKey).toBeDefined() + }) + + it('should create DID with didDocument and calls updateDid', async () => { + const createDidRequest = { + state: {}, + signingRequest: { serializedPayload: new Uint8Array() }, + } as unknown as CreateDIDRequest + + mockFunction(createOrGetKey).mockResolvedValueOnce({ keyId: mockKeyId, publicJwk: mockPublicJwk }) + mockFunction(generateCreateDIDRequest).mockResolvedValueOnce(createDidRequest) + mockFunction(submitCreateDIDRequest).mockResolvedValueOnce({ did, didDocument }) + + const updateDidSpy = jest.spyOn(service, 'updateDid').mockResolvedValueOnce({ did, didDocument }) + + const result = await service.createDid(mockAgentContext, { + method: 'hedera', + options: { network: 'testnet' }, + secret: { rootKeyId: mockKeyId, keys: [] }, + didDocument: DidDocument.fromJSON(didDocument), + }) + + expect(updateDidSpy).toHaveBeenCalled() + expect(result.rootKey).toBeDefined() + }) + }) + + describe('updateDid', () => { + it('should throw error if didDocumentOperation is missing', async () => { + await expect(service.updateDid(mockAgentContext, { did, didDocument })).rejects.toThrow( + 'DidDocumentOperation is required' + ) + }) + + it('should throw error if rootKey missing', async () => { + const keys: DidDocumentKey[] = [] + await expect( + service.updateDid(mockAgentContext, { + did, + didDocumentOperation: 'setDidDocument', + secret: { keys }, + didDocument: {}, + }) + ).rejects.toThrow('The root key not found in the KMS') + }) + + it('should call correct builder methods for each field and action', () => { + const spies = { + addService: jest.spyOn(builder, 'addService'), + removeService: jest.spyOn(builder, 'removeService'), + addVerificationMethod: jest.spyOn(builder, 'addVerificationMethod'), + removeVerificationMethod: jest.spyOn(builder, 'removeVerificationMethod'), + addAssertionMethod: jest.spyOn(builder, 'addAssertionMethod'), + removeAssertionMethod: jest.spyOn(builder, 'removeAssertionMethod'), + addAuthenticationMethod: jest.spyOn(builder, 'addAuthenticationMethod'), + removeAuthenticationMethod: jest.spyOn(builder, 'removeAuthenticationMethod'), + addCapabilityDelegationMethod: jest.spyOn(builder, 'addCapabilityDelegationMethod'), + removeCapabilityDelegationMethod: jest.spyOn(builder, 'removeCapabilityDelegationMethod'), + addCapabilityInvocationMethod: jest.spyOn(builder, 'addCapabilityInvocationMethod'), + removeCapabilityInvocationMethod: jest.spyOn(builder, 'removeCapabilityInvocationMethod'), + addKeyAgreementMethod: jest.spyOn(builder, 'addKeyAgreementMethod'), + removeKeyAgreementMethod: jest.spyOn(builder, 'removeKeyAgreementMethod'), + } + + const testCases: [string, 'add' | 'remove', string, jest.SpyInstance][] = [ + ['service', 'add', 'service-item', spies.addService], + ['service', 'remove', 'service-id', spies.removeService], + + ['verificationMethod', 'add', 'verificationMethod-item', spies.addVerificationMethod], + ['verificationMethod', 'remove', 'verificationMethod-id', spies.removeVerificationMethod], + + ['assertionMethod', 'add', 'assertionMethod-item', spies.addAssertionMethod], + ['assertionMethod', 'remove', 'assertionMethod-id', spies.removeAssertionMethod], + + ['authentication', 'add', 'authentication-item', spies.addAuthenticationMethod], + ['authentication', 'remove', 'authentication-id', spies.removeAuthenticationMethod], + + ['capabilityDelegation', 'add', 'capabilityDelegation-item', spies.addCapabilityDelegationMethod], + ['capabilityDelegation', 'remove', 'capabilityDelegation-id', spies.removeCapabilityDelegationMethod], + + ['capabilityInvocation', 'add', 'capabilityInvocation-item', spies.addCapabilityInvocationMethod], + ['capabilityInvocation', 'remove', 'capabilityInvocation-id', spies.removeCapabilityInvocationMethod], + + ['keyAgreement', 'add', 'keyAgreement-item', spies.addKeyAgreementMethod], + ['keyAgreement', 'remove', 'keyAgreement-id', spies.removeKeyAgreementMethod], + ] + + for (const [property, action, param, spy] of testCases) { + jest.clearAllMocks() + + // biome-ignore lint/suspicious/noExplicitAny: + const builderMethod = (service as any).getUpdateMethod(builder, property, action) + + const result = builderMethod(param) + + expect(result).toBe(builder) + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith(param) + for (const otherSpy of Object.values(spies)) { + if (otherSpy !== spy) expect(otherSpy).not.toHaveBeenCalled() + } + } + }) + + it('should return builder unchanged for unknown property', () => { + const unknownProperty = 'unknown-property' + + // biome-ignore lint/suspicious/noExplicitAny: + const builderMethod = (service as any).getUpdateMethod(builder, unknownProperty, 'add') + const result = builderMethod({}) + + expect(result).toBe(builder) + }) + + it('should perform update flow successfully', async () => { + const keys: DidDocumentKey[] = [ + { kmsKeyId: mockKeyId, didDocumentRelativeKeyId: DID_ROOT_KEY_ID }, + { kmsKeyId: 'some-key', didDocumentRelativeKeyId: '#abc' }, + ] + + const updatedDidDocument = { + ...didDocument, + verificationMethod: [ + ...didDocument.verificationMethod, + { + id: '#abc', + type: 'Ed25519VerificationKey2020' as const, + controller: 'test', + publicKeyMultibase: 'mock-public-key-multibase', + }, + ], + } + + const updateDidRequest = { + state: {}, + signingRequest: { serializedPayload: new Uint8Array() }, + } as unknown as UpdateDIDRequest + + // @ts-ignore - there is a conflict with 'resolveDID' "overloaded" signatures + mockFunction(resolveDID).mockResolvedValueOnce({ didDocument }) + + jest + // biome-ignore lint/suspicious/noExplicitAny: + .spyOn(service as any, 'prepareDidUpdates') + .mockReturnValueOnce({ build: jest.fn().mockReturnValueOnce(updatedDidDocument) }) + + mockFunction(generateUpdateDIDRequest).mockResolvedValueOnce(updateDidRequest) + + // biome-ignore lint/suspicious/noExplicitAny: + jest.spyOn(service as any, 'signRequests').mockResolvedValueOnce(Promise.resolve()) + mockFunction(submitUpdateDIDRequest).mockResolvedValueOnce({ did, didDocument: updatedDidDocument }) + + await expect( + service.updateDid(mockAgentContext, { + did, + didDocumentOperation: 'setDidDocument', + didDocument: updatedDidDocument, + secret: { keys }, + }) + ).resolves.toHaveProperty('did', did) + + // biome-ignore lint/suspicious/noExplicitAny: + expect((service as any).prepareDidUpdates).toHaveBeenCalled() + expect(generateUpdateDIDRequest).toHaveBeenCalled() + expect(submitUpdateDIDRequest).toHaveBeenCalled() + }) + }) + + describe('deactivateDid', () => { + it('should throw error if rootKey is missing', async () => { + await expect(service.deactivateDid(mockAgentContext, { did, secret: { keys: [] } })).rejects.toThrow( + 'The root key not found in the KMS' + ) + }) + + it('should throw an error if root key is not found in deactivateDid', async () => { + mockFunction(mockAgentContext.dependencyManager.resolve).mockReturnValueOnce({ sign: jest.fn() }) + + await expect( + service.deactivateDid(mockAgentContext, { + did, + secret: { + keys: [], + }, + }) + ).rejects.toThrow('The root key not found in the KMS') + }) + + it('should deactivate DID successfully', async () => { + const deactivateDidRequest = { + state: {}, + signingRequest: { serializedPayload: new Uint8Array() }, + } as unknown as DeactivateDIDRequest + + mockFunction(generateDeactivateDIDRequest).mockResolvedValueOnce(deactivateDidRequest) + mockFunction(submitDeactivateDIDRequest).mockResolvedValueOnce({ + did, + didDocument, + }) + + const result = await service.deactivateDid(mockAgentContext, { + did, + secret: { keys: [{ kmsKeyId: mockKeyId, didDocumentRelativeKeyId: DID_ROOT_KEY_ID }] }, + }) + + expect(result).toHaveProperty('did', did) + expect(mockKms.sign).toHaveBeenCalledWith({ + keyId: mockKeyId, + data: deactivateDidRequest.signingRequest.serializedPayload, + algorithm: 'EdDSA', + }) + }) + }) + + describe('anoncreds SDK methods', () => { + it('getSchema', async () => { + const result = await service.getSchema(mockAgentContext, 'schemaId') + expect(mockHederaAnonCredsRegistry.getSchema).toHaveBeenCalledWith('schemaId') + expect(result).toBe('schema') + }) + + it('registerSchema', async () => { + const options: RegisterSchemaOptions = { + schema: { + issuerId: '', + name: '', + version: '', + attrNames: [], + }, + options: {}, + } + const result = await service.registerSchema(mockAgentContext, options) + expect(mockHederaAnonCredsRegistry.registerSchema).toHaveBeenCalledWith({ + ...options, + issuerKeyDer: expect.anything(), + }) + expect(result).toBe('registerSchema') + }) + + it('getCredentialDefinition', async () => { + const result = await service.getCredentialDefinition(mockAgentContext, 'credDefId') + expect(mockHederaAnonCredsRegistry.getCredentialDefinition).toHaveBeenCalledWith('credDefId') + expect(result).toBe('credDef') + }) + + it('registerCredentialDefinition', async () => { + const options: RegisterCredentialDefinitionOptions = { + options: { + supportRevocation: true, + }, + credentialDefinition: { + issuerId: '', + schemaId: '', + type: 'CL', + tag: '', + value: { + primary: {}, + revocation: undefined, + }, + }, + } + await service.registerCredentialDefinition(mockAgentContext, options) + expect(mockHederaAnonCredsRegistry.registerCredentialDefinition).toHaveBeenCalledWith({ + ...options, + issuerKeyDer: expect.anything(), + options: { + supportRevocation: true, + }, + }) + }) + + it('getRevocationRegistryDefinition', async () => { + const result = await service.getRevocationRegistryDefinition(mockAgentContext, 'revRegDefId') + expect(mockHederaAnonCredsRegistry.getRevocationRegistryDefinition).toHaveBeenCalledWith('revRegDefId') + expect(result).toBe('revRegDef') + }) + + it('registerRevocationRegistryDefinition', async () => { + const options: RegisterRevocationRegistryDefinitionOptions = { + revocationRegistryDefinition: { + issuerId: '', + revocDefType: 'CL_ACCUM', + credDefId: '', + tag: '', + value: { + publicKeys: { + accumKey: { + z: '', + }, + }, + maxCredNum: 0, + tailsLocation: '', + tailsHash: '', + }, + }, + options: {}, + } + const result = await service.registerRevocationRegistryDefinition(mockAgentContext, options) + expect(mockHederaAnonCredsRegistry.registerRevocationRegistryDefinition).toHaveBeenCalledWith({ + ...options, + issuerKeyDer: expect.anything(), + }) + expect(result).toBe('registerRevRegDef') + }) + + it('getRevocationStatusList', async () => { + const result = await service.getRevocationStatusList(mockAgentContext, 'revRegId', 12345) + expect(mockHederaAnonCredsRegistry.getRevocationStatusList).toHaveBeenCalledWith('revRegId', 12345) + expect(result).toBe('revStatusList') + }) + + it('registerRevocationStatusList', async () => { + const options: RegisterRevocationStatusListOptions = { + options: {}, + revocationStatusList: { + revRegDefId: '', + issuerId: '', + revocationList: [], + currentAccumulator: '', + }, + } + const result = await service.registerRevocationStatusList(mockAgentContext, options) + expect(mockHederaAnonCredsRegistry.registerRevocationStatusList).toHaveBeenCalledWith({ + ...options, + issuerKeyDer: expect.anything(), + }) + expect(result).toBe('registerRevStatus') + }) + }) + + describe('getIssuerPrivateKey', () => { + it('should return PrivateKey from secretBytes when rootKey exists', async () => { + const secretBytes = PrivateKey.generate().toBytes() + const keyInfo = { key: { secretBytes } } + + const didRecord = { + keys: [{ didDocumentRelativeKeyId: DID_ROOT_KEY_ID, kmsKeyId: 'kms-key-id' }], + } as unknown as DidRecord + + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValueOnce(didRecord) + // @ts-ignore + mockFunction(mockKeyManagementService.getKeyAsserted).mockResolvedValue(keyInfo) + + // biome-ignore lint/suspicious/noExplicitAny: + const result = await (service as any).getIssuerPrivateKey(mockAgentContext, 'issuer-id') + + expect(mockDidRepository.findCreatedDid).toHaveBeenCalledWith(mockAgentContext, 'issuer-id') + // @ts-ignore + expect(mockKms.getKms).toHaveBeenCalledWith(mockAgentContext, 'askar') + // @ts-ignore + expect(mockKeyManagementService.getKeyAsserted).toHaveBeenCalledWith(mockAgentContext, 'kms-key-id') + expect(result).toEqual(PrivateKey.fromBytesED25519(secretBytes)) + }) + + it('should throw error if no rootKey found', async () => { + const didRecord = { + keys: [], + } as unknown as DidRecord + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(didRecord) + + // biome-ignore lint/suspicious/noExplicitAny: + await expect((service as any).getIssuerPrivateKey(mockAgentContext, 'issuer-id')).rejects.toThrow( + 'The root key not found in the KMS' + ) + }) + + it('should throw error if didRecord is null or undefined', async () => { + mockFunction(mockDidRepository.findCreatedDid).mockResolvedValue(null) + + // biome-ignore lint/suspicious/noExplicitAny: + await expect((service as any).getIssuerPrivateKey(mockAgentContext, 'issuer-id')).rejects.toThrow( + 'The root key not found in the KMS' + ) + }) + }) +}) diff --git a/packages/hedera/tests/unit/kms-publisher.test.ts b/packages/hedera/tests/unit/kms-publisher.test.ts new file mode 100644 index 0000000000..42aa1adf23 --- /dev/null +++ b/packages/hedera/tests/unit/kms-publisher.test.ts @@ -0,0 +1,124 @@ +import { AgentContext, Kms, TypedArrayEncoder } from '@credo-ts/core' +import { KmsJwkPublicOkp } from '@credo-ts/core/src/modules/kms' +import { KeysUtility } from '@hiero-did-sdk/core' +import { KmsPublisher } from '../../src/ledger/publisher/KmsPublisher' + +jest.mock('@hiero-did-sdk/core', () => ({ + KeysUtility: { + fromBytes: jest.fn(), + }, + DIDError: class DIDError extends Error {}, +})) + +jest.mock('@credo-ts/core', () => ({ + TypedArrayEncoder: { + fromBase64: jest.fn(), + }, + Kms: { + KeyManagementApi: jest.fn().mockImplementation(() => ({})), + }, +})) + +jest.mock('../../src/ledger/utils', () => ({ + createOrGetKey: jest.fn(), +})) +import { createOrGetKey } from '../../src/ledger/utils' + +jest.mock('@hiero-did-sdk/publisher-internal', () => { + return { + Publisher: jest.fn(), + } +}) + +describe('KmsPublisher', () => { + const mockClient = { + freezeWith: jest.fn(), + signWith: jest.fn(), + execute: jest.fn(), + operator: { + accountId: '0.0.1234', + publicKey: {}, + }, + } + + const mockFrozenTransaction = { + signWith: jest.fn(), + } + + const mockResponse = { + getReceipt: jest.fn(), + } + + const signMock = jest.fn().mockResolvedValue({ signature: 'signature-bytes' }) + + const kmsMock = { + sign: signMock, + } + + const agentContext = { + dependencyManager: { + resolve: jest.fn().mockImplementation((key) => { + if (key === Kms.KeyManagementApi) { + return kmsMock + } + throw new Error(`Unexpected dependency: ${key}`) + }), + }, + } + + const keyId = 'test-key-id' + const base64X = 'base64-x' + const publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } = { x: base64X, crv: 'Ed25519', kty: 'OKP' } + const key: { keyId: string; publicJwk: KmsJwkPublicOkp & { crv: 'Ed25519' } } = { keyId, publicJwk } + + const mockPublicKey = { + toPublicKey: jest.fn(), + } + + const fakePublicKey = {} + + beforeEach(() => { + jest.clearAllMocks() + ;(TypedArrayEncoder.fromBase64 as jest.Mock).mockReturnValue(new Uint8Array([1, 2, 3])) + ;(KeysUtility.fromBytes as jest.Mock).mockReturnValue(mockPublicKey) + mockPublicKey.toPublicKey.mockReturnValue(fakePublicKey) + + mockClient.freezeWith.mockReturnValue(mockFrozenTransaction) + + mockFrozenTransaction.signWith.mockImplementation(async (_publicKey, signCallback) => { + const signature = await signCallback(new Uint8Array([4, 5, 6])) + expect(signature).toBe('signature-bytes') + return + }) + + mockClient.execute.mockResolvedValue(mockResponse) + + mockResponse.getReceipt.mockResolvedValue('receipt-object') + }) + + it('should correctly create an instance via constructor', () => { + // biome-ignore lint/suspicious/noExplicitAny: + const publisher = new KmsPublisher(agentContext as any, mockClient as any, key) + expect(agentContext.dependencyManager.resolve).toHaveBeenCalledWith(expect.anything()) + expect(publisher.publicKey()).toBe(fakePublicKey) + }) + + it('should correctly update key in setKeyId', async () => { + ;(createOrGetKey as jest.Mock).mockResolvedValue({ + publicJwk: { x: base64X, crv: 'Ed25519' }, + }) + + // biome-ignore lint/suspicious/noExplicitAny: + const publisher = new KmsPublisher(agentContext as unknown as AgentContext, mockClient as any, key) + await publisher.setKeyId('new-key-id') + + expect(createOrGetKey).toHaveBeenCalledWith(kmsMock, 'new-key-id') + expect(KeysUtility.fromBytes).toHaveBeenCalledTimes(2) + }) + + it('should return correct publicKey', () => { + // biome-ignore lint/suspicious/noExplicitAny: + const publisher = new KmsPublisher(agentContext as unknown as AgentContext, mockClient as any, key) + expect(publisher.publicKey()).toBe(fakePublicKey) + }) +}) diff --git a/packages/hedera/tests/unit/utils.test.ts b/packages/hedera/tests/unit/utils.test.ts new file mode 100644 index 0000000000..1b2901bf8a --- /dev/null +++ b/packages/hedera/tests/unit/utils.test.ts @@ -0,0 +1,85 @@ +import { Kms } from '@credo-ts/core' +import type { KeyManagementApi, KmsJwkPublicOkp, KmsJwkPublicRsa } from '@credo-ts/core/src/modules/kms' +import { createOrGetKey, getMultibasePublicKey } from '../../src/ledger/utils' + +describe('getMultibasePublicKey', () => { + it('should return a base58 key string prefixed with "z"', () => { + const base64X = 'dGVzdGtleQ==' // base64 for 'testkey' + const publicJwk = { + crv: 'Ed25519', + x: base64X, + } + const multibaseKey = getMultibasePublicKey(publicJwk as KmsJwkPublicOkp & { crv: 'Ed25519' }) + + expect(multibaseKey.startsWith('z')).toBe(true) + expect(typeof multibaseKey).toBe('string') + }) +}) + +describe('createOrGetKey', () => { + let kmsMock: jest.Mocked + + beforeEach(() => { + kmsMock = { + createKey: jest.fn(), + getPublicKey: jest.fn(), + } as unknown as jest.Mocked + }) + + it('should create a key if keyId is not provided', async () => { + const fakeKeyId = 'key123' + const fakeJwk: KmsJwkPublicOkp & { kid: string } = { kty: 'OKP', crv: 'Ed25519', x: 'xxx', kid: 'key123' } + kmsMock.createKey.mockResolvedValue({ + keyId: fakeKeyId, + publicJwk: fakeJwk, + }) + + const result = await createOrGetKey(kmsMock, undefined) + + expect(kmsMock.createKey).toHaveBeenCalledWith({ type: { crv: 'Ed25519', kty: 'OKP' } }) + expect(result).toEqual({ + keyId: fakeKeyId, + publicJwk: fakeJwk, + }) + }) + + it('should retrieve an existing key if keyId is provided', async () => { + const keyId = 'key456' + const publicJwk: KmsJwkPublicOkp & { kid: string } = { kty: 'OKP', crv: 'Ed25519', x: 'xxx', kid: 'key123' } + kmsMock.getPublicKey.mockResolvedValue(publicJwk) + + const result = await createOrGetKey(kmsMock, keyId) + + expect(kmsMock.getPublicKey).toHaveBeenCalledWith({ keyId }) + expect(result).toEqual({ + keyId, + publicJwk: { + ...publicJwk, + crv: publicJwk.crv, + }, + }) + }) + + it('should throw an error if key with given keyId is not found', async () => { + // @ts-ignore + kmsMock.getPublicKey.mockResolvedValue(null) + + // Expect the function to throw an error for a missing key + await expect(createOrGetKey(kmsMock, 'notfound')).rejects.toThrowError("Key with key id 'notfound' not found") + }) + + it('should throw an error if key has unsupported kty or crv', async () => { + const keyId = 'badkey' + const badJwk: KmsJwkPublicRsa & { kid: string } = { e: '', kid: 'key-1', n: '', kty: 'RSA' } + + kmsMock.getPublicKey.mockResolvedValue(badJwk) + + const spyDesc = jest.spyOn(Kms, 'getJwkHumanDescription').mockReturnValue('unsupported key type') + + await expect(createOrGetKey(kmsMock, keyId)).rejects.toThrow( + `Key with key id '${keyId}' uses unsupported unsupported key type for did:hedera` + ) + + spyDesc.mockRestore() + }) +}) diff --git a/packages/hedera/tsconfig.build.json b/packages/hedera/tsconfig.build.json new file mode 100644 index 0000000000..e7ee40ad18 --- /dev/null +++ b/packages/hedera/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.build.json", + "compilerOptions": { + "outDir": "./build", + "baseUrl": ".", + "skipDefaultLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["../core"] +} diff --git a/packages/hedera/tsconfig.json b/packages/hedera/tsconfig.json new file mode 100644 index 0000000000..dcd84a3af2 --- /dev/null +++ b/packages/hedera/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["jest"], + "moduleResolution": "node" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c73a0244ca..748523b3d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -195,12 +195,18 @@ importers: '@credo-ts/didcomm': specifier: workspace:* version: link:../packages/didcomm + '@credo-ts/hedera': + specifier: workspace:* + version: link:../packages/hedera '@credo-ts/indy-vdr': specifier: workspace:* version: link:../packages/indy-vdr '@credo-ts/node': specifier: workspace:* version: link:../packages/node + '@hiero-did-sdk/client': + specifier: 0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) '@types/figlet': specifier: ^1.5.4 version: 1.5.8 @@ -663,6 +669,52 @@ importers: specifier: 'catalog:' version: 5.8.3 + packages/hedera: + dependencies: + '@credo-ts/anoncreds': + specifier: workspace:* + version: link:../anoncreds + '@credo-ts/core': + specifier: workspace:* + version: link:../core + '@hashgraph/sdk': + specifier: ^2.72.0 + version: 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/anoncreds': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/client': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/hcs': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/publisher-internal': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/registrar': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/resolver': + specifier: ^0.1.3 + version: 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + devDependencies: + '@credo-ts/node': + specifier: workspace:* + version: link:../node + '@hyperledger/anoncreds-nodejs': + specifier: ^0.3.1 + version: 0.3.1 + rimraf: + specifier: ^4.4.0 + version: 4.4.1 + zstd-napi: + specifier: ^0.0.10 + version: 0.0.10 + packages/indy-sdk-to-askar-migration: dependencies: '@credo-ts/anoncreds': @@ -1940,6 +1992,36 @@ packages: '@bufbuild/protobuf@2.2.5': resolution: {integrity: sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': + resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==} + cpu: [arm64] + os: [darwin] + + '@cbor-extract/cbor-extract-darwin-x64@2.2.0': + resolution: {integrity: sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==} + cpu: [x64] + os: [darwin] + + '@cbor-extract/cbor-extract-linux-arm64@2.2.0': + resolution: {integrity: sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==} + cpu: [arm64] + os: [linux] + + '@cbor-extract/cbor-extract-linux-arm@2.2.0': + resolution: {integrity: sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==} + cpu: [arm] + os: [linux] + + '@cbor-extract/cbor-extract-linux-x64@2.2.0': + resolution: {integrity: sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==} + cpu: [x64] + os: [linux] + + '@cbor-extract/cbor-extract-win32-x64@2.2.0': + resolution: {integrity: sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==} + cpu: [x64] + os: [win32] + '@changesets/apply-release-plan@7.0.12': resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} @@ -2308,6 +2390,60 @@ packages: cpu: [x64] os: [win32] + '@ethersproject/abi@5.8.0': + resolution: {integrity: sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==} + + '@ethersproject/abstract-provider@5.8.0': + resolution: {integrity: sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==} + + '@ethersproject/abstract-signer@5.8.0': + resolution: {integrity: sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==} + + '@ethersproject/address@5.8.0': + resolution: {integrity: sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==} + + '@ethersproject/base64@5.8.0': + resolution: {integrity: sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==} + + '@ethersproject/bignumber@5.8.0': + resolution: {integrity: sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==} + + '@ethersproject/bytes@5.8.0': + resolution: {integrity: sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==} + + '@ethersproject/constants@5.8.0': + resolution: {integrity: sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==} + + '@ethersproject/hash@5.8.0': + resolution: {integrity: sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==} + + '@ethersproject/keccak256@5.8.0': + resolution: {integrity: sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==} + + '@ethersproject/logger@5.8.0': + resolution: {integrity: sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==} + + '@ethersproject/networks@5.8.0': + resolution: {integrity: sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==} + + '@ethersproject/properties@5.8.0': + resolution: {integrity: sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==} + + '@ethersproject/rlp@5.8.0': + resolution: {integrity: sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==} + + '@ethersproject/signing-key@5.8.0': + resolution: {integrity: sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==} + + '@ethersproject/strings@5.8.0': + resolution: {integrity: sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==} + + '@ethersproject/transactions@5.8.0': + resolution: {integrity: sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==} + + '@ethersproject/web@5.8.0': + resolution: {integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==} + '@expo/bunyan@4.0.0': resolution: {integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==} engines: {'0': node >=0.10.0} @@ -2381,6 +2517,15 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@grpc/grpc-js@1.13.4': + resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + '@hapi/bourne@3.0.0': resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} @@ -2390,6 +2535,81 @@ packages: '@hapi/topo@5.1.0': resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + '@hashgraph/cryptography@1.9.0': + resolution: {integrity: sha512-0UItolO1W/f8YIsGBrIxvjY+cSdvs4sEdzXOL49ThYEfPskJUprG3vhMhosRFoA4d0hxdJ7/glB7f7He8RW9xg==} + engines: {node: '>=12.0.0'} + peerDependencies: + expo-crypto: '*' + peerDependenciesMeta: + expo-crypto: + optional: true + + '@hashgraph/proto@2.22.0': + resolution: {integrity: sha512-+h2qqk+KwpV+rr1AN4ip1Gel3X4v0DvFO9WH7o0ZR3gQX9pfzurptKGs30DlBnH21xPqDH61v90bZvVknE27NA==} + engines: {node: '>=10.0.0'} + + '@hashgraph/sdk@2.72.0': + resolution: {integrity: sha512-w35M77OAkJutENG4CldUGzfT+qubDjEYCQR5Ran75uHB+SLeCodR87AXWJ3ocr5vPaZ7lsflBXEYZLhgCi1G2g==} + engines: {node: '>=18.0.0'} + peerDependencies: + bn.js: ^5.2.1 + + '@hiero-did-sdk/anoncreds@0.1.3': + resolution: {integrity: sha512-SUeUdHFbZTHF5fTGZTcMqrAFfQTQBdFhFvUCf95Yv1MGYMaLEZfPaCs/x95/TjJFN1j9F0nZlvEkJnW56OoGow==} + engines: {node: '>=20'} + + '@hiero-did-sdk/cache@0.1.3': + resolution: {integrity: sha512-ucmuFhZNZwI3BRzxUU+x7T50n4O7t6Y9dRTxNM7cQOYajwyGTNDuR/2Mp87DqvlGXKEj8Yj5JYE/1hX+fdZViQ==} + engines: {node: '>=20'} + + '@hiero-did-sdk/client@0.1.3': + resolution: {integrity: sha512-DE/xiQCz83zlxO1pmouy+wSvxbMBsKPgfCRTW0Kr7FuVqnBP6ihCbNfdPU9Sj9jgVyGQIey+UPNRiY5lzYYvrA==} + engines: {node: '>=20'} + + '@hiero-did-sdk/core@0.1.3': + resolution: {integrity: sha512-v0O2ssemyBVZuduwjojdHL66YCo322Z+xGIt7cd2UCNu8beztGF5eRJxY+W+mCX/H3m8lcxqL2hAcF04KmhEjQ==} + engines: {node: '>=20'} + + '@hiero-did-sdk/crypto@0.1.3': + resolution: {integrity: sha512-xQnJXHPSFVT56kz2EHqBRVPUqrqNPDmRRYHaeqctvm+62cjKhYv+ja/w7CWamoMlobLe1bzU1KZqVQDWqBDUbQ==} + engines: {node: '>=20'} + + '@hiero-did-sdk/hcs@0.1.3': + resolution: {integrity: sha512-Gm43RLGDUpGfc/olCMQEMgZXP11ZZt8WMsb/7VaN5fNhxf6qE2y2k7V9thcJSqrSvJ2nvZeWDzGnCcb4VIj+pg==} + engines: {node: '>=20'} + + '@hiero-did-sdk/lifecycle@0.1.3': + resolution: {integrity: sha512-A2Mlsxtx8XZAUxpcNBnHvNENdlC0vNaSDU8eFXpxpA9B7LP/Bx/WaH0RYOW1nZBiXMaApBGLbZoJPTTbWRE5Mg==} + engines: {node: '>=20'} + + '@hiero-did-sdk/messages@0.1.3': + resolution: {integrity: sha512-5WNmNbxNDRTTsfjObE09Su7ERBAVjQNcKFgPC0oBvDhyEE3cknYcBkc9P+G8Uoee/+zxebfZR9KTISqbvdQ9lw==} + engines: {node: '>=20'} + + '@hiero-did-sdk/publisher-internal@0.1.3': + resolution: {integrity: sha512-qGsV3no4jUo/R0VzcDar3hZ+IiaHJVnhdM6fmrUPNikbnVhTTNmJ5g2ci0Ed6NkXhPCuaJtcCK4SgGdEB6jOrQ==} + engines: {node: '>=20'} + + '@hiero-did-sdk/registrar@0.1.3': + resolution: {integrity: sha512-EDuuNRzkgLbWXacqaF59q7Do3U1p1w11g33FaJNWJ0gCd0S6imrxwUfDE/6THQjhSOVtfkqzo8Ge2Vm/pLQUQg==} + engines: {node: '>=20'} + + '@hiero-did-sdk/resolver@0.1.3': + resolution: {integrity: sha512-TfSCcC0gGU/jgiJK5qr6cwW1Mpmj9vYwIo24YygUxXP47+wPQ4suk/K5FlAsPOq8oga47RwLWVFX+ys66Y2r1w==} + engines: {node: '>=20'} + + '@hiero-did-sdk/signer-internal@0.1.3': + resolution: {integrity: sha512-W5yMxz5VSnJA6B5M02YggbhcSDgQnTK50lBv5koTF534gR0vRSuX49i02EqxUrBB3NTahZ7vWNhr+YrP6t6Akw==} + engines: {node: '>=20'} + + '@hiero-did-sdk/verifier-internal@0.1.3': + resolution: {integrity: sha512-ybGwSU+9rA9pIOeGkCCiFIcbgG/WvW85igBj2wWQPCwH9G9c6qW/alHX5Az/9C7O//f2/CzVaHeVVFQ1CTcLBg==} + engines: {node: '>=20'} + + '@hiero-did-sdk/zstd@0.1.3': + resolution: {integrity: sha512-s+kqRhNbtMhGA7X6CtiTgGpLGqVAflxdPAJ5PqblgSGosa6m23SIe9rTC5mRkOziJ4NeoYvx2/NuosT2faSJKw==} + engines: {node: '>=20'} + '@hyperledger/anoncreds-nodejs@0.3.1': resolution: {integrity: sha512-/oWmWgcOPqjAtd2+dKASPYL84Qd7sAFyCBfEKM7PAgVbObaZUZc0kqA7hkEz/qyiqUvcP/JwKTc1v4zVZi6BTg==} engines: {node: '>= 18'} @@ -2549,6 +2769,9 @@ packages: peerDependencies: '@js-joda/core': '>=1.11.0' + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + '@koa/bodyparser@5.1.1': resolution: {integrity: sha512-ZBF49xqNVxnmJ+8iXegq+fXPQm9RSX8giNl/aXS5rW1VpNct92wnFbGR/47vfoRJVLARGQ4HVL4WaQ0u8IJVoA==} engines: {node: '>= 16'} @@ -2846,8 +3069,8 @@ packages: resolution: {integrity: sha512-lzD84av1ZQhYUS+jsGqJiCMaJO2dn9u+RTT9n9q6D3SaKVwWqv+7AoRKqBu19bkwyE+iFRl1ymr40QS90jVFYg==} engines: {node: '>=14.15'} - '@scure/base@1.2.1': - resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==} + '@scure/base@1.2.6': + resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} '@sd-jwt/core@0.10.0': resolution: {integrity: sha512-EuFsIHP76fwNi97dGcz2jdEenHL/AkDGcqrEA00k82Uw0HP/hvbAfB+yyPxYrd3dVaxe5PWSKvDkgDK6kKk+6Q==} @@ -3405,6 +3628,10 @@ packages: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} engines: {node: '>= 4.0.0'} + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -3692,6 +3919,13 @@ packages: canonicalize@2.0.0: resolution: {integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==} + cbor-extract@2.2.0: + resolution: {integrity: sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==} + hasBin: true + + cbor-x@1.6.0: + resolution: {integrity: sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -3710,6 +3944,9 @@ packages: charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -3819,6 +4056,9 @@ packages: colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -3962,6 +4202,9 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + crypto-ld@6.0.0: resolution: {integrity: sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA==} engines: {node: '>=8.3.0'} @@ -4001,6 +4244,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dayjs@1.11.11: resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==} @@ -4381,6 +4627,10 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4451,6 +4701,9 @@ packages: fast-base64-decode@1.0.0: resolution: {integrity: sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==} + fast-copy@3.0.2: + resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -4464,6 +4717,10 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} @@ -4595,6 +4852,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + forge-light@1.1.4: + resolution: {integrity: sha512-Nr0xdu93LJawgBZVU/tC+A+4pbKqigdY5PRBz8CXNm4e5saAZIqU2Qe9+nVFtVO5TWCHSgvI0LaZZuatgE5J1g==} + engines: {node: '>= 6.13.0'} + form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -4630,6 +4891,9 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -4730,6 +4994,9 @@ packages: resolution: {integrity: sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==} engines: {node: '>=6'} + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -4840,6 +5107,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hermes-estree@0.19.1: resolution: {integrity: sha512-daLGV3Q2MKk8w4evNMKwS8zBE/rcpA800nu1Q5kM08IKijoSnPe9Uo1iIxzPKRkn95IxxsgBMPeYHt3VG4ej2g==} @@ -5412,9 +5682,16 @@ packages: jose@5.10.0: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-base64@3.7.7: resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} + js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -5649,6 +5926,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -5848,7 +6128,6 @@ packages: metro-react-native-babel-preset@0.73.10: resolution: {integrity: sha512-1/dnH4EHwFb2RKEKx34vVDpUS3urt2WEeR8FYim+ogqALg4sTpG7yeQPxWpbgKATezt4rNfqAANpIyH19MS4BQ==} - deprecated: Use @react-native/babel-preset instead peerDependencies: '@babel/core': '*' @@ -6017,6 +6296,9 @@ packages: resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} engines: {node: '>= 18'} + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -6079,6 +6361,9 @@ packages: engines: {node: ^18 || >=20} hasBin: true + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -6110,12 +6395,19 @@ packages: resolution: {integrity: sha512-R49fALR9caB6vxuSWUIaK2eBYeTloZQUFBZ4rHO+TbhMGQHtwnhdqKLYki+o+8qMgLvoBYWrp/2KzGPhxL4S6w==} engines: {node: '>=18.20.0 <20 || >=20.12.1'} + node-abi@3.75.0: + resolution: {integrity: sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==} + engines: {node: '>=10'} + node-addon-api@3.2.1: resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} node-addon-api@5.1.0: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-dir@0.1.17: resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==} engines: {node: '>= 0.10.5'} @@ -6146,6 +6438,10 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-gyp-build-optional-packages@5.1.1: + resolution: {integrity: sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==} + hasBin: true + node-gyp-build@4.8.1: resolution: {integrity: sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==} hasBin: true @@ -6228,6 +6524,10 @@ packages: resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} engines: {node: ^10.13.0 || >=12.0.0} + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -6436,6 +6736,20 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pino-abstract-transport@2.0.0: + resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + + pino-pretty@13.1.1: + resolution: {integrity: sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==} + hasBin: true + + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + + pino@9.9.0: + resolution: {integrity: sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==} + hasBin: true + pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -6464,6 +6778,11 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -6492,6 +6811,9 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@5.0.0: + resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -6517,6 +6839,10 @@ packages: resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} hasBin: true + protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + engines: {node: '>=12.0.0'} + protobufjs@7.4.0: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} @@ -6538,9 +6864,6 @@ packages: pure-rand@6.1.0: resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - pvtsutils@1.3.5: - resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} - pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} @@ -6573,6 +6896,9 @@ packages: queue@6.0.2: resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -6676,6 +7002,10 @@ packages: readonly-date@1.0.0: resolution: {integrity: sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==} + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + recast@0.21.5: resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} engines: {node: '>= 4'} @@ -6797,6 +7127,9 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfc4648@1.5.4: + resolution: {integrity: sha512-rRg/6Lb+IGfJqO05HZkN50UtY7K/JhxJag1kP23+zyMfrvoB0B7RWv06MbOzoc79RgCdNTiUaNsTT1AJZ7Z+cg==} + rimraf@2.2.8: resolution: {integrity: sha512-R5KMKHnPAQaZMqLOsyuyUmcIjSeDm+73eoqQpaXA7AZ22BL+6C+1mcUscgOsNd8WVlJuvlgAPsegcx7pjlV0Dg==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -6867,6 +7200,10 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safe-stable-stringify@2.5.0: + resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} + engines: {node: '>=10'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -6880,6 +7217,9 @@ packages: resolution: {integrity: sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==} engines: {node: '>=18.0.0'} + secure-json-parse@4.0.0: + resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} + selfsigned@2.4.1: resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} engines: {node: '>=10'} @@ -6989,6 +7329,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + simple-plist@1.3.1: resolution: {integrity: sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==} @@ -7007,6 +7353,9 @@ packages: resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} engines: {node: '>=8.0.0'} + sonic-boom@4.2.0: + resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -7029,6 +7378,9 @@ packages: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} + spark-md5@3.0.2: + resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==} + spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} @@ -7036,6 +7388,10 @@ packages: resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} engines: {node: '>=6'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + split@1.0.1: resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} @@ -7155,6 +7511,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + strnum@1.0.5: resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} @@ -7189,6 +7549,7 @@ packages: superagent@10.2.1: resolution: {integrity: sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==} engines: {node: '>=14.18.0'} + deprecated: Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net supertest@7.1.1: resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==} @@ -7219,6 +7580,13 @@ packages: resolution: {integrity: sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==} engines: {node: '>=0.10'} + tar-fs@2.1.3: + resolution: {integrity: sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -7278,6 +7646,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thread-stream@3.1.0: + resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + throat@5.0.0: resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==} @@ -7392,6 +7763,12 @@ packages: resolution: {integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==} engines: {node: '>= 6.0.0'} + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + type-check@0.3.2: resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} engines: {node: '>= 0.8.0'} @@ -7833,6 +8210,10 @@ packages: zod@3.25.67: resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + zstd-napi@0.0.10: + resolution: {integrity: sha512-pwnG+auSiIrD2BNSIpPEUtcRSK33cfYmKo3sJPTohFiPqPci9F4SIRPR7gGeI45Maj4nFoyyxzT2YDxVXIIgzQ==} + engines: {node: ^12.22.0 || ^14.17.0 || ^15.12.0 || >=16} + snapshots: '@2060.io/ffi-napi@4.0.9': @@ -8991,6 +9372,24 @@ snapshots: '@bufbuild/protobuf@2.2.5': {} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-darwin-x64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-arm64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-arm@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-linux-x64@2.2.0': + optional: true + + '@cbor-extract/cbor-extract-win32-x64@2.2.0': + optional: true + '@changesets/apply-release-plan@7.0.12': dependencies: '@changesets/config': 3.1.1 @@ -9621,6 +10020,129 @@ snapshots: '@esbuild/win32-x64@0.25.5': optional: true + '@ethersproject/abi@5.8.0': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/constants': 5.8.0 + '@ethersproject/hash': 5.8.0 + '@ethersproject/keccak256': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/strings': 5.8.0 + + '@ethersproject/abstract-provider@5.8.0': + dependencies: + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/networks': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/transactions': 5.8.0 + '@ethersproject/web': 5.8.0 + + '@ethersproject/abstract-signer@5.8.0': + dependencies: + '@ethersproject/abstract-provider': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + + '@ethersproject/address@5.8.0': + dependencies: + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/rlp': 5.8.0 + + '@ethersproject/base64@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + + '@ethersproject/bignumber@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + bn.js: 5.2.1 + + '@ethersproject/bytes@5.8.0': + dependencies: + '@ethersproject/logger': 5.8.0 + + '@ethersproject/constants@5.8.0': + dependencies: + '@ethersproject/bignumber': 5.8.0 + + '@ethersproject/hash@5.8.0': + dependencies: + '@ethersproject/abstract-signer': 5.8.0 + '@ethersproject/address': 5.8.0 + '@ethersproject/base64': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/strings': 5.8.0 + + '@ethersproject/keccak256@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + js-sha3: 0.8.0 + + '@ethersproject/logger@5.8.0': {} + + '@ethersproject/networks@5.8.0': + dependencies: + '@ethersproject/logger': 5.8.0 + + '@ethersproject/properties@5.8.0': + dependencies: + '@ethersproject/logger': 5.8.0 + + '@ethersproject/rlp@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + + '@ethersproject/signing-key@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + bn.js: 5.2.1 + elliptic: 6.6.1 + hash.js: 1.1.7 + + '@ethersproject/strings@5.8.0': + dependencies: + '@ethersproject/bytes': 5.8.0 + '@ethersproject/constants': 5.8.0 + '@ethersproject/logger': 5.8.0 + + '@ethersproject/transactions@5.8.0': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/constants': 5.8.0 + '@ethersproject/keccak256': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/rlp': 5.8.0 + '@ethersproject/signing-key': 5.8.0 + + '@ethersproject/web@5.8.0': + dependencies: + '@ethersproject/base64': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/properties': 5.8.0 + '@ethersproject/strings': 5.8.0 + '@expo/bunyan@4.0.0': dependencies: uuid: 8.3.2 @@ -9906,6 +10428,18 @@ snapshots: dependencies: graphql: 15.8.0 + '@grpc/grpc-js@1.13.4': + dependencies: + '@grpc/proto-loader': 0.7.15 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.1 + protobufjs: 7.4.0 + yargs: 17.7.2 + '@hapi/bourne@3.0.0': {} '@hapi/hoek@9.3.0': @@ -9916,6 +10450,190 @@ snapshots: '@hapi/hoek': 9.3.0 optional: true + '@hashgraph/cryptography@1.9.0(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@noble/curves': 1.9.2 + asn1js: 3.0.6 + bignumber.js: 9.1.2 + bn.js: 5.2.1 + buffer: 6.0.3 + crypto-js: 4.2.0 + forge-light: 1.1.4 + js-base64: 3.7.7 + react-native-get-random-values: 1.11.0(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + spark-md5: 3.0.2 + tweetnacl: 1.0.3 + utf8: 3.0.0 + transitivePeerDependencies: + - react-native + + '@hashgraph/proto@2.22.0': + dependencies: + long: 5.3.1 + protobufjs: 7.2.5 + + '@hashgraph/sdk@2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@ethersproject/abi': 5.8.0 + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/rlp': 5.8.0 + '@grpc/grpc-js': 1.13.4 + '@hashgraph/cryptography': 1.9.0(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hashgraph/proto': 2.22.0 + bignumber.js: 9.1.2 + bn.js: 5.2.1 + crypto-js: 4.2.0 + js-base64: 3.7.7 + long: 5.3.1 + pino: 9.9.0 + pino-pretty: 13.1.1 + protobufjs: 7.2.5 + rfc4648: 1.5.4 + utf8: 3.0.0 + transitivePeerDependencies: + - expo-crypto + - react-native + + '@hiero-did-sdk/anoncreds@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/hcs': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/zstd': 0.1.3 + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/cache@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/client@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/core@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@scure/base': 1.2.6 + buffer: 6.0.3 + cbor-x: 1.6.0 + varint: 6.0.0 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/crypto@0.1.3': + dependencies: + buffer: 6.0.3 + + '@hiero-did-sdk/hcs@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/cache': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/client': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/crypto': 0.1.3 + '@hiero-did-sdk/zstd': 0.1.3 + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/lifecycle@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/messages@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/lifecycle': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/resolver': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/publisher-internal@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/registrar@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/lifecycle': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/messages': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/publisher-internal': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/resolver': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/signer-internal': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/verifier-internal': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/resolver@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/client': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/hcs': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/verifier-internal': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/signer-internal@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/verifier-internal@0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1))': + dependencies: + '@hashgraph/sdk': 2.72.0(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + '@hiero-did-sdk/core': 0.1.3(bn.js@5.2.1)(react-native@0.79.4(@babel/core@7.27.7)(@react-native-community/cli@10.2.7(@babel/core@7.27.7))(react@18.3.1)) + buffer: 6.0.3 + transitivePeerDependencies: + - bn.js + - expo-crypto + - react-native + + '@hiero-did-sdk/zstd@0.1.3': {} + '@hyperledger/anoncreds-nodejs@0.3.1': dependencies: '@2060.io/ffi-napi': 4.0.9 @@ -10200,6 +10918,8 @@ snapshots: dependencies: '@js-joda/core': 5.6.3 + '@js-sdsl/ordered-map@4.4.2': {} + '@koa/bodyparser@5.1.1(koa@2.16.0)': dependencies: co-body: 6.2.0 @@ -10741,7 +11461,7 @@ snapshots: metro: 0.82.4 metro-config: 0.82.4 metro-core: 0.82.4 - semver: 7.6.2 + semver: 7.7.2 optionalDependencies: '@react-native-community/cli': 10.2.7(@babel/core@7.27.7) transitivePeerDependencies: @@ -10818,7 +11538,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@scure/base@1.2.1': {} + '@scure/base@1.2.6': {} '@sd-jwt/core@0.10.0': dependencies: @@ -11418,7 +12138,7 @@ snapshots: asn1js@3.0.5: dependencies: - pvtsutils: 1.3.5 + pvtsutils: 1.3.6 pvutils: 1.1.3 tslib: 2.8.1 @@ -11449,6 +12169,8 @@ snapshots: at-least-node@1.0.0: {} + atomic-sleep@1.0.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -11864,6 +12586,22 @@ snapshots: canonicalize@2.0.0: {} + cbor-extract@2.2.0: + dependencies: + node-gyp-build-optional-packages: 5.1.1 + optionalDependencies: + '@cbor-extract/cbor-extract-darwin-arm64': 2.2.0 + '@cbor-extract/cbor-extract-darwin-x64': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm': 2.2.0 + '@cbor-extract/cbor-extract-linux-arm64': 2.2.0 + '@cbor-extract/cbor-extract-linux-x64': 2.2.0 + '@cbor-extract/cbor-extract-win32-x64': 2.2.0 + optional: true + + cbor-x@1.6.0: + optionalDependencies: + cbor-extract: 2.2.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -11881,6 +12619,8 @@ snapshots: charenc@0.0.2: {} + chownr@1.1.4: {} + chownr@2.0.0: {} chownr@3.0.0: {} @@ -11989,6 +12729,8 @@ snapshots: colorette@1.4.0: optional: true + colorette@2.0.20: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -12156,6 +12898,8 @@ snapshots: crypt@0.0.2: {} + crypto-js@4.2.0: {} + crypto-ld@6.0.0: {} crypto-random-string@1.0.0: {} @@ -12191,6 +12935,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + dateformat@4.6.3: {} + dayjs@1.11.11: optional: true @@ -12307,7 +13053,7 @@ snapshots: '@noble/ciphers': 1.2.1 '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 - '@scure/base': 1.2.1 + '@scure/base': 1.2.6 canonicalize: 2.0.0 did-resolver: 4.1.0 multibase: 4.0.6 @@ -12605,6 +13351,8 @@ snapshots: exit@0.1.2: {} + expand-template@2.0.3: {} + expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -12749,6 +13497,8 @@ snapshots: fast-base64-decode@1.0.0: {} + fast-copy@3.0.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -12763,6 +13513,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} fast-text-encoding@1.0.6: {} @@ -12917,6 +13669,8 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + forge-light@1.1.4: {} + form-data-encoder@2.1.4: {} form-data@3.0.1: @@ -12951,6 +13705,8 @@ snapshots: fresh@0.5.2: {} + fs-constants@1.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -13073,6 +13829,8 @@ snapshots: getenv@1.0.0: {} + github-from-package@0.0.0: {} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -13209,6 +13967,8 @@ snapshots: dependencies: function-bind: 1.1.2 + help-me@5.0.0: {} + hermes-estree@0.19.1: {} hermes-estree@0.25.1: {} @@ -14013,8 +14773,12 @@ snapshots: jose@5.10.0: {} + joycon@3.1.1: {} + js-base64@3.7.7: {} + js-sha3@0.8.0: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -14268,6 +15032,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} lodash.defaults@4.2.0: {} @@ -14917,6 +15683,8 @@ snapshots: minipass: 7.1.2 rimraf: 5.0.10 + mkdirp-classic@0.5.3: {} + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -14970,6 +15738,8 @@ snapshots: nanoid@5.0.9: {} + napi-build-utils@2.0.0: {} + natural-compare@1.4.0: {} ncp@2.0.0: @@ -14994,10 +15764,16 @@ snapshots: json-stringify-safe: 5.0.1 propagate: 2.0.1 + node-abi@3.75.0: + dependencies: + semver: 7.7.2 + node-addon-api@3.2.1: {} node-addon-api@5.1.0: {} + node-addon-api@7.1.1: {} + node-dir@0.1.17: dependencies: minimatch: 3.1.2 @@ -15023,6 +15799,11 @@ snapshots: node-forge@1.3.1: {} + node-gyp-build-optional-packages@5.1.1: + dependencies: + detect-libc: 2.0.3 + optional: true + node-gyp-build@4.8.1: {} node-int64@0.4.0: {} @@ -15110,6 +15891,8 @@ snapshots: oidc-token-hash@5.0.3: {} + on-exit-leak-free@2.1.2: {} + on-finished@2.3.0: dependencies: ee-first: 1.1.1 @@ -15307,6 +16090,42 @@ snapshots: pify@4.0.1: {} + pino-abstract-transport@2.0.0: + dependencies: + split2: 4.2.0 + + pino-pretty@13.1.1: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 3.0.2 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pump: 3.0.0 + secure-json-parse: 4.0.0 + sonic-boom: 4.2.0 + strip-json-comments: 5.0.3 + + pino-std-serializers@7.0.0: {} + + pino@9.9.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 + process-warning: 5.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.2.0 + safe-stable-stringify: 2.5.0 + sonic-boom: 4.2.0 + thread-stream: 3.1.0 + pirates@4.0.6: {} pkg-dir@3.0.0: @@ -15333,6 +16152,21 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.0 + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.75.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.3 + tunnel-agent: 0.6.0 + prelude-ls@1.1.2: {} prettier@2.8.8: {} @@ -15363,6 +16197,8 @@ snapshots: process-nextick-args@2.0.1: optional: true + process-warning@5.0.0: {} + progress@2.0.3: {} promise@7.3.1: @@ -15402,6 +16238,21 @@ snapshots: '@types/node': 20.19.2 long: 4.0.0 + protobufjs@7.2.5: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 20.19.2 + long: 5.3.1 + protobufjs@7.4.0: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -15415,7 +16266,7 @@ snapshots: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/node': 20.19.2 - long: 5.2.3 + long: 5.3.1 proxy-addr@2.0.7: dependencies: @@ -15433,10 +16284,6 @@ snapshots: pure-rand@6.1.0: {} - pvtsutils@1.3.5: - dependencies: - tslib: 2.8.1 - pvtsutils@1.3.6: dependencies: tslib: 2.8.1 @@ -15468,6 +16315,8 @@ snapshots: dependencies: inherits: 2.0.4 + quick-format-unescaped@4.0.4: {} + quick-lru@5.1.1: {} quick-lru@7.0.0: {} @@ -15619,6 +16468,8 @@ snapshots: readonly-date@1.0.0: {} + real-require@0.2.0: {} + recast@0.21.5: dependencies: ast-types: 0.15.2 @@ -15753,6 +16604,8 @@ snapshots: reusify@1.0.4: {} + rfc4648@1.5.4: {} + rimraf@2.2.8: optional: true @@ -15823,6 +16676,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} sax@1.4.1: {} @@ -15835,6 +16690,8 @@ snapshots: node-addon-api: 5.1.0 node-gyp-build: 4.8.1 + secure-json-parse@4.0.0: {} + selfsigned@2.4.1: dependencies: '@types/node-forge': 1.3.11 @@ -15977,6 +16834,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + simple-plist@1.3.1: dependencies: bplist-creator: 0.1.0 @@ -15996,6 +16861,10 @@ snapshots: slugify@1.6.6: {} + sonic-boom@4.2.0: + dependencies: + atomic-sleep: 1.0.0 + source-map-js@1.2.0: {} source-map-support@0.5.13: @@ -16015,6 +16884,8 @@ snapshots: source-map@0.7.4: optional: true + spark-md5@3.0.2: {} + spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -16022,6 +16893,8 @@ snapshots: split-on-first@1.1.0: {} + split2@4.2.0: {} + split@1.0.1: dependencies: through: 2.3.8 @@ -16135,6 +17008,8 @@ snapshots: strip-json-comments@3.1.1: {} + strip-json-comments@5.0.3: {} + strnum@1.0.5: optional: true @@ -16209,6 +17084,21 @@ snapshots: symbol-observable@2.0.3: {} + tar-fs@2.1.3: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -16285,6 +17175,10 @@ snapshots: dependencies: any-promise: 1.3.0 + thread-stream@3.1.0: + dependencies: + real-require: 0.2.0 + throat@5.0.0: {} through2@2.0.5: @@ -16390,6 +17284,12 @@ snapshots: dependencies: tslib: 1.14.1 + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + tweetnacl@1.0.3: {} + type-check@0.3.2: dependencies: prelude-ls: 1.1.2 @@ -16803,3 +17703,9 @@ snapshots: zod: 3.25.67 zod@3.25.67: {} + + zstd-napi@0.0.10: + dependencies: + '@types/node': 20.19.2 + node-addon-api: 7.1.1 + prebuild-install: 7.1.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8da71a7c33..944c8e8d31 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -44,3 +44,5 @@ onlyBuiltDependencies: - "@hyperledger/anoncreds-nodejs" - "@hyperledger/indy-vdr-nodejs" - "@openwallet-foundation/askar-nodejs" + - "zstd-napi" + - "cbor-extract"