diff --git a/package-lock.json b/package-lock.json index d987db436..5c9277bc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13037,7 +13037,8 @@ }, "node_modules/pako": { "version": "2.1.0", - "license": "(MIT AND Zlib)" + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" }, "node_modules/parent-module": { "version": "1.0.1", diff --git a/src/domain/backup/index.ts b/src/domain/backup/index.ts index c926d91e4..917361f14 100644 --- a/src/domain/backup/index.ts +++ b/src/domain/backup/index.ts @@ -1,10 +1,17 @@ import { Schema as v0_0_1 } from "./v0_0_1"; +import { Schema as v0_0_2 } from "./v0_0_2"; /** * All supported backup schemas */ -export type Schema = v0_0_1; +export type Schema = v0_0_1 | v0_0_2; +export type Version = "0.0.1" | "0.0.2"; export const defaultVersion = "0.0.1"; -export { v0_0_1 }; +export const versions: Version[] = [ + "0.0.1", + "0.0.2", +]; + +export { v0_0_1, v0_0_2 }; diff --git a/src/domain/backup/v0_0_2.ts b/src/domain/backup/v0_0_2.ts new file mode 100644 index 000000000..493d8aa36 --- /dev/null +++ b/src/domain/backup/v0_0_2.ts @@ -0,0 +1,45 @@ +/** + * Schema definition for Backup V0.0.2 + */ +import * as TB from "@sinclair/typebox"; + +const credential = TB.Object({ + recovery_id: TB.String(), + data: TB.String(), +}); + +const did = TB.Object({ + did: TB.String(), + alias: TB.Optional(TB.String()), +}); + +const didpair = TB.Object({ + holder: TB.String(), + recipient: TB.String(), + alias: TB.String(), +}); + +const key = TB.Object({ + recovery_id: TB.String(), + key: TB.String(), + did: TB.Optional(TB.String()), + index: TB.Optional(TB.Number()), +}); + + +export const Schema = TB.Object({ + version: TB.Optional(TB.Literal("0.0.2")), + credentials: TB.Array(credential), + dids: TB.Array(did), + did_pairs: TB.Array(didpair), + keys: TB.Array(key), +}); + +export type Schema = TB.Static; + +export namespace Schema { + export type Credential = TB.Static; + export type DID = TB.Static; + export type DIDPair = TB.Static; + export type Key = TB.Static; +} diff --git a/src/domain/buildingBlocks/Pluto.ts b/src/domain/buildingBlocks/Pluto.ts index 950b681ff..cc9da685a 100644 --- a/src/domain/buildingBlocks/Pluto.ts +++ b/src/domain/buildingBlocks/Pluto.ts @@ -41,7 +41,7 @@ export interface Pluto { /** * create a Backup object from the stored data */ - backup(): Promise; + backup(version?: Backup.Version): Promise; /** * load the given data into the store diff --git a/src/edge-agent/Agent.Backup.ts b/src/edge-agent/Agent.Backup.ts index f21c11b16..b51c2b0f0 100644 --- a/src/edge-agent/Agent.Backup.ts +++ b/src/edge-agent/Agent.Backup.ts @@ -1,57 +1,94 @@ +import Pako from "pako"; import * as Domain from "../domain"; import Agent from "./Agent"; +import { Version } from "../domain/backup"; import { isObject, validateSafe } from "../utils"; + /** * define Agent requirements for Backup */ type BackupAgent = Pick; +type MasterKey = Domain.PrivateKey & Domain.ExportableKey.Common & Domain.ExportableKey.JWK & Domain.ExportableKey.PEM + +export type BackupOptions = { + version?: Version + key?: MasterKey + compress?: boolean +} + export class AgentBackup { constructor( public readonly Agent: BackupAgent ) {} /** - * create JWE of data stored in Pluto + * Creates a JWE (JSON Web Encryption) containing the backup data stored in Pluto. + * The data can optionally be encrypted using a custom master key and can be compressed. + * + * @param {BackupOptions} [options] - Optional settings for the backup. + * @param {Version} [options.version] - Specifies the version of the backup data. + * @param {MasterKey} [options.key] - Custom master key used for encrypting the backup. + * @param {boolean} [options.compress] - If true, compresses the JWE using DEFLATE. + * + * @returns {Promise} - A promise that resolves to the JWE string. * - * @returns {string} - * @see restore + * @see restore - Method to restore data from a JWE string. */ - async createJWE(): Promise { + async createJWE(options?: BackupOptions): Promise { await this.Agent.pollux.start(); - const backup = await this.Agent.pluto.backup(); - const masterSk = await this.masterSk(); + + const backup = await this.Agent.pluto.backup(options?.version); + const backupStr = options?.compress ? this.compress(JSON.stringify(backup)) : JSON.stringify(backup); + const masterSk = options?.key ?? await this.masterSk(); const jwk = masterSk.to.JWK(); - const jwe = this.Agent.pollux.jwe.JWE.encrypt( - JSON.stringify(backup), + + return this.Agent.pollux.jwe.JWE.encrypt( + backupStr, JSON.stringify(jwk), - 'backup' + 'backup', ); - return jwe; } /** - * decode JWE and save data to store + * Decodes a JWE (JSON Web Encryption) string and restores the backup data to the store. + * If the JWE is compressed (Base64-encoded), it will attempt to decompress it first. + * + * @param {string} jwe - The JWE string containing the encrypted backup data. + * @param {MasterKey} [key] - Optional custom master key used for decrypting the JWE. + * If not provided, the default master key is used. + * + * @returns {Promise} - A promise that resolves when the data is successfully restored. * - * @param jwe - * @see backup + * @see createJWE - Method to create a JWE from the stored backup data. */ - async restore(jwe: string) { + async restore(jwe: string, key?: MasterKey) { await this.Agent.pollux.start(); - const masterSk = await this.masterSk(); + const masterSk = key ?? await this.masterSk(); + const jwk = masterSk.to.JWK(); const decoded = this.Agent.pollux.jwe.JWE.decrypt( jwe, 'backup', - JSON.stringify(jwk) + JSON.stringify(jwk), ); - const jsonStr = Buffer.from(decoded).toString(); - const json = JSON.parse(jsonStr); - const backup = this.parseBackupJson(json); + let jsonStr: string; + try { + jsonStr = this.decopress(new TextDecoder().decode(decoded)); + } catch { + jsonStr = Buffer.from(decoded).toString(); + } + const json = JSON.parse(jsonStr); + const backup = this.parseBackupJson(json); await this.Agent.pluto.restore(backup); } + getStringByteLength(str: string) { + const encoder = new TextEncoder(); + return encoder.encode(str).length; + } + private parseBackupJson(json: unknown): Domain.Backup.Schema { if (isObject(json)) { const version = json.version ?? Domain.Backup.defaultVersion; @@ -60,12 +97,47 @@ export class AgentBackup { if (validateSafe(json, Domain.Backup.v0_0_1)) { return json; } + break; + case "0.0.2": + if (validateSafe(json, Domain.Backup.v0_0_2)) { + return json; + } } } throw new Domain.AgentError.BackupVersionError(); } + /** + * Compresses a JSON object into a Base64-encoded string using DEFLATE. + * + * - Uses `level: 9` for maximum compression and `strategy: Z_FILTERED` + * (optimized for repetitive patterns, common in JSON data). + * - Converts the JSON to a string, compresses it, and encodes it in Base64. + * + * @param {unknown} json - The JSON object to compress. + * @returns {string} - The Base64-encoded compressed string. + */ + private compress(json: unknown): string { + // Strategy 1 is + return Buffer.from(Pako.deflate(JSON.stringify(json), {level: 9, strategy: 1})).toString('base64'); + } + + /** + * Decompresses a Base64-encoded string into its original JSON representation. + * + * - Decodes the Base64 string to a binary buffer. + * - Uses DEFLATE to decompress the data and converts it back to a JSON string. + * - Parses and returns the JSON object. + * + * @param {string} data - The Base64-encoded compressed string. + * @returns {string} - The decompressed JSON string. + */ + private decopress(data: string): string { + const compressedData = Buffer.from(data, 'base64'); + return JSON.parse(Pako.inflate(compressedData, {to: 'string'})); + } + /** * create a JWK for the MasterKey (X25519) * @returns JWK diff --git a/src/pluto/Pluto.ts b/src/pluto/Pluto.ts index 24b782959..7e40ea8d4 100644 --- a/src/pluto/Pluto.ts +++ b/src/pluto/Pluto.ts @@ -5,6 +5,7 @@ import { PeerDID } from "../peer-did/PeerDID"; import { BackupManager } from "./backup/BackupManager"; import { PlutoRepositories, repositoryFactory } from "./repositories"; import { Arrayable, asArray } from "../utils"; +import { Version } from "../domain/backup"; /** @@ -121,8 +122,8 @@ export class Pluto implements Domain.Pluto { } /** Backups **/ - backup() { - return this.BackupMgr.backup(); + backup(version?: Version) { + return this.BackupMgr.backup(version); } restore(backup: Domain.Backup.Schema) { diff --git a/src/pluto/backup/BackupManager.ts b/src/pluto/backup/BackupManager.ts index e7252c2ef..8fa23e09f 100644 --- a/src/pluto/backup/BackupManager.ts +++ b/src/pluto/backup/BackupManager.ts @@ -4,6 +4,7 @@ import { Pluto } from "../Pluto"; import { PlutoRepositories } from "../repositories"; import { isEmpty } from "../../utils"; import { IBackupTask, IRestoreTask } from "./versions/interfaces"; +import { Version } from "../../domain/backup"; /** * BackupManager @@ -21,7 +22,7 @@ export class BackupManager { * @param version - backup schema version * @returns {Promise} */ - backup(version?: string) { + backup(version?: Version) { const task = this.getBackupTask(version); return task.run(); } @@ -37,21 +38,24 @@ export class BackupManager { await task.run(); } - private getBackupTask(version: string = Domain.Backup.defaultVersion): IBackupTask { + private getBackupTask(version: Version = Domain.Backup.defaultVersion): IBackupTask { switch (version) { case "0.0.1": return new Versions.v0_0_1.BackupTask(this.Pluto, this.Repositories); + case "0.0.2": + return new Versions.v0_0_2.BackupTask(this.Pluto, this.Repositories); } throw new Domain.PlutoError.BackupNotFoundError(); } private getRestoreTask(backup: Domain.Backup.Schema): IRestoreTask { - const version = backup.version ?? Domain.Backup.defaultVersion; - - switch (version) { + backup.version = backup.version ?? Domain.Backup.defaultVersion; + switch (backup.version) { case "0.0.1": return new Versions.v0_0_1.RestoreTask(this.Pluto, backup); + case "0.0.2": + return new Versions.v0_0_2.RestoreTask(this.Pluto, backup); } throw new Domain.PlutoError.RestoreNotFoundError(); diff --git a/src/pluto/backup/versions/0_0_1/Backup.ts b/src/pluto/backup/versions/0_0_1/Backup.ts index cb4f35433..e1dc8b171 100644 --- a/src/pluto/backup/versions/0_0_1/Backup.ts +++ b/src/pluto/backup/versions/0_0_1/Backup.ts @@ -68,7 +68,6 @@ export class BackupTask implements IBackupTask { index: key.index, did: did?.uuid, }; - return acc.concat(backup); } diff --git a/src/pluto/backup/versions/0_0_2/Backup.ts b/src/pluto/backup/versions/0_0_2/Backup.ts new file mode 100644 index 000000000..ba1136cc5 --- /dev/null +++ b/src/pluto/backup/versions/0_0_2/Backup.ts @@ -0,0 +1,97 @@ +import * as Domain from "../../../../domain"; +import * as Models from "../../../models"; +import { JWTVerifiableCredentialRecoveryId } from "../../../../pollux/models/JWTVerifiableCredential"; +import { repositoryFactory } from "../../../repositories/builders/factory"; +import { IBackupTask } from "../interfaces"; +import { SDJWTVerifiableCredentialRecoveryId } from "../../../../pollux/models/SDJWTVerifiableCredential"; +import { base64url } from "multiformats/bases/base64"; + +export class BackupTask implements IBackupTask { + constructor( + private readonly Pluto: Domain.Pluto, + private readonly Repositories: ReturnType + ) {} + + async run(): Promise { + const credentials = await this.getCredentialBackups(); + const didModels = await this.Repositories.DIDs.getModels(); + const dids = didModels.map(this.mapDid); + const did_pairs = await this.getDidPairBackups(); + const keys = await this.getKeyBackups(didModels); + + const json: Domain.Backup.v0_0_2 = { + version: "0.0.2", + credentials, + dids, + did_pairs, + keys, + }; + + return json; + } + + async getCredentialBackups(): Promise { + const credentialModels = await this.Repositories.Credentials.getModels(); + return credentialModels.map(this.mapCredential); + } + + async getDidPairBackups(): Promise { + const pairLinks = await this.Repositories.DIDLinks.getModels({ selector: { role: Models.DIDLink.role.pair } }); + const didTuples = pairLinks.map(link => ({ + alias: link.alias ?? "", + holder: link.hostId, + recipient: link.targetId + })); + + return didTuples; + } + + async getKeyBackups(didModels: Models.DID[]): Promise { + const keys = await this.Repositories.Keys.get(); + const didKeyLinks = await this.Repositories.DIDKeyLinks.getModels(); + + const backupKeys = keys.reduce((acc, key) => { + if (key.isExportable() && key.isStorable()) { + const keyLink = didKeyLinks.find(x => x.keyId === key.uuid); + const did = didModels.find(x => x.uuid === keyLink?.didId); + const jwk = key.to.JWK(); + + const backup: Domain.Backup.v0_0_1.Key = { + recovery_id: key.recoveryId, + key: base64url.baseEncode(Buffer.from(JSON.stringify(jwk))), + index: key.index, + did: did?.uuid, + }; + return acc.concat(backup); + } + + return acc; + }, []); + + return backupKeys; + } + + async getLinkSecretBackup(): Promise { + const linksecret = await this.Repositories.LinkSecrets.findOne(); + + return linksecret?.secret ?? undefined; + } + + + private mapCredential = (model: Models.Credential): Domain.Backup.v0_0_1.Credential => { + const isJWT = model.recoveryId === JWTVerifiableCredentialRecoveryId; + const isSDJWT = model.recoveryId === SDJWTVerifiableCredentialRecoveryId; + const recoveryId = isJWT ? "jwt" : isSDJWT ? "sdjwt" : "anoncred"; + const data = isJWT || isSDJWT ? JSON.parse(model.dataJson).id : model.dataJson; + + return { + recovery_id: recoveryId, + data: base64url.baseEncode(Buffer.from(data)), + }; + }; + + private mapDid = (model: Models.DID): Domain.Backup.v0_0_1.DID => ({ + did: model.uuid, + alias: model.alias + }); +} diff --git a/src/pluto/backup/versions/0_0_2/Restore.ts b/src/pluto/backup/versions/0_0_2/Restore.ts new file mode 100644 index 000000000..91d808181 --- /dev/null +++ b/src/pluto/backup/versions/0_0_2/Restore.ts @@ -0,0 +1,91 @@ +import * as Domain from "../../../../domain"; +import { Ed25519PrivateKey } from "../../../../apollo/utils/Ed25519PrivateKey"; +import { Secp256k1PrivateKey } from "../../../../apollo/utils/Secp256k1PrivateKey"; +import { X25519PrivateKey } from "../../../../apollo/utils/X25519PrivateKey"; +import { AnonCredsCredential } from "../../../../pollux/models/AnonCredsVerifiableCredential"; +import { JWTCredential } from "../../../../pollux/models/JWTVerifiableCredential"; +import { notEmptyString, notNil, validate } from "../../../../utils"; +import { IRestoreTask } from "../interfaces"; +import { base64url } from "multiformats/bases/base64"; + +export class RestoreTask implements IRestoreTask { + constructor( + private readonly Pluto: Domain.Pluto, + private readonly backup: Domain.Backup.v0_0_2, + ) { } + + async run(): Promise { + validate(this.backup, Domain.Backup.v0_0_2); + await this.restoreCredentials(); + await this.restoreDids(); + await this.restoreDidPairs(); + await this.restoreKeys(); + } + + async restoreCredentials() { + const credentials = this.backup.credentials.map(item => { + const decoded = Buffer.from(base64url.baseDecode(item.data)).toString() + if (item.recovery_id === "jwt") { + return JWTCredential.fromJWS(decoded); + } + if (item.recovery_id === "anoncred") { + return AnonCredsCredential.fromJson(decoded); + } + throw new Domain.PlutoError.RestoreCredentialInvalidError(); + }); + + await Promise.all(credentials.map(x => this.Pluto.storeCredential(x))); + } + + async restoreDids() { + await Promise.all( + this.backup.dids.map(x => this.Pluto.storeDID(Domain.DID.from(x.did), [], x.alias)) + ); + } + + async restoreDidPairs() { + await Promise.all( + this.backup.did_pairs.map(item => { + const host = Domain.DID.from(item.holder); + const target = Domain.DID.from(item.recipient); + return this.Pluto.storeDIDPair(host, target, item.alias); + }) + ); + } + + async restoreKeys() { + return Promise.all(this.backup.keys.map(item => { + + const jwk = JSON.parse(Buffer.from(base64url.baseDecode(item.key)).toString()); + const key = this.jwkToDomain(jwk); + if (notNil(item.index)) { + key.keySpecification.set(Domain.KeyProperties.index, item.index.toString()); + } + if (notEmptyString(item.did)) { + return this.Pluto.storeDID(Domain.DID.from(item.did), key); + } else { + return this.Pluto.storePrivateKey(key); + } + }) + ) + } + + private jwkToDomain(jwk: Domain.JWK): Domain.PrivateKey { + if ((jwk.kty === "OKP" || jwk.kty === "EC") && notEmptyString(jwk.d)) { + switch (jwk.crv) { + case Domain.Curve.SECP256K1.toLowerCase(): + return Secp256k1PrivateKey.from.String(jwk.d, "base64url"); + + case Domain.Curve.ED25519: + return Ed25519PrivateKey.from.String(jwk.d, "base64url"); + + case Domain.Curve.X25519: + return X25519PrivateKey.from.String(jwk.d, "base64url"); + } + + throw new Domain.PlutoError.RestoreKeyInvalidError(); + } + + throw new Domain.PlutoError.RestoreJWKInvalidError(); + } +} diff --git a/src/pluto/backup/versions/0_0_2/index.ts b/src/pluto/backup/versions/0_0_2/index.ts new file mode 100644 index 000000000..9bf29dabe --- /dev/null +++ b/src/pluto/backup/versions/0_0_2/index.ts @@ -0,0 +1,2 @@ +export * from "./Backup"; +export * from "./Restore"; diff --git a/src/pluto/backup/versions/index.ts b/src/pluto/backup/versions/index.ts index e78f30a59..9f73ac8b0 100644 --- a/src/pluto/backup/versions/index.ts +++ b/src/pluto/backup/versions/index.ts @@ -1,3 +1,4 @@ import * as v0_0_1 from "./0_0_1"; +import * as v0_0_2 from "./0_0_2"; -export { v0_0_1 }; +export { v0_0_1, v0_0_2 }; diff --git a/tests/agent/Agent.test.ts b/tests/agent/Agent.test.ts index d1d1a50ff..ade3f5475 100644 --- a/tests/agent/Agent.test.ts +++ b/tests/agent/Agent.test.ts @@ -45,7 +45,7 @@ import InMemoryStore from "../fixtures/inmemory"; import { ApiResponse, Pluto as IPluto } from "../../src/domain"; import { Pluto } from "../../src/pluto/Pluto"; import { RevocationNotification } from "../../src/edge-agent/protocols/revocation/RevocationNotfiication"; -import { BasicMediatorHandler, Castor, Store } from "../../src"; +import { Castor, Store } from "../../src"; import { randomUUID } from "crypto"; import { JWT } from "../../src/pollux/utils/JWT"; @@ -260,39 +260,46 @@ describe("Agent Tests", () => { beforeEach(async () => { await agent.start(); }); + /* Iterate through backup scenarios and fixtures to validate backup and restore functionality. + Each fixture tests the following: + - `backup`: Ensures a valid JWE is created from backup data. + - `restore`: Verifies the restoration of backup data into the store. + - `round trip integration`: Confirms data integrity during backup and restore cycle. + */ + Fixtures.Backup.backups.forEach(backupFixture => { + describe(`Backup/Restore :: ${backupFixture.title}`, () => { + + test("backup", async () => { + sandbox.stub(pluto, "backup").resolves(backupFixture.json); + + const jwe = await agent.backup.createJWE(backupFixture.options); + expect(jwe).to.be.a("string"); + expect(jwe.split(".")).to.have.length(5); + }); - describe("Backup/Restore", () => { - test("backup", async () => { - sandbox.stub(pluto, "backup").resolves(Fixtures.Backup.backupJson); - - const jwe = await agent.backup.createJWE(); - - expect(jwe).to.be.a("string"); - expect(jwe.split(".")).to.have.length(5); - }); + test("restore", async () => { + const stubRestore = sandbox.stub(pluto, "restore"); + await agent.backup.restore(backupFixture.jwe, backupFixture.options.key); + const expected = JSON.parse(JSON.stringify(backupFixture.json)); + expect(stubRestore).to.have.been.calledWith(expected); + }); - test("restore", async () => { - const stubRestore = sandbox.stub(pluto, "restore"); - const backupJWE = "eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYXB2IjoiVk5BTmhuZFl6dmdXdkVhRjlZNHllNVNYRXJCLXZSZkRTRjhfX0o2ZlVUTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiRXJ4dDRKYkJqa1FzSklEZGxUSzFNZEczUC1JYXJ0aGRuM01YWmxLWGkwbyJ9fQ.J4khuugTiYpjjwrGwHYc3JFxr9J33OwLwdCOotf6hGg5uBmhSRzpYqIHgt_hbZa9ot49RD6klDlmMcWf0M9K5xV5T41UIAkG.UVppJucK7jm2p2FNuGjw_g.S7I6FiS2Fo6SbYN0txF0iaFApKrY899fvUYe40Ymu_S-xq4-_LDM_FWoSPyc1KHL1bzYhPLAMeuKlr1FGecq6YGPkWu0hsz32j5gAsgVGSVoljwg8fCdmVO4D0ijjTmBHVvbOBBHeQsjAWe9udbLD6uOcbksizkPHEePZqZhBX5jOCfX-l6K3g0jP2FKRStpR4_zXyomThlp64WFWK023XXlwqR3rL4rY1ru8Ago-ux7Vo-SS14x9k82e5Ca2M8AwV1NSU2nIRQcruDz-YyInc4yWVcOSI3UnGZD6nvKHsV8_K4IEYviR6w1uTXN_PohfounUJFyUyaYXD4PmsgXggHb5wC3fKuG3NRcjZm6LraWt4__5cxY21P_mvK1VkIdkdo0rUdVHLAeqbxCxJ-8-5mmuaO5t75-NjfiT0P2eTuSYpiUxeEOPpro6_YoFtOv30CjeU-5hQSU1lQ1fA2iRaaMjOgR1PUUgRB7AcYy5wlkqsf_L0o8cl_y-BU93_9JuGHbVDShDaI2SfND19IguddhBnPHj0QL_6VCmC6313RiGXERC8NZz-f9NfGO7qHjY3CaRsU4Txw-zOPud5SFAkiHx4DzaVXB4KPcsEzQdfr5WUrYxVxP0UZP5hiyXiC1GlhwLm_Y0mBExQ9x1pnRyi8HaIl0toTlr40dcLEaT1iDe_IIVZREARy_hlCdzUXyWl7Zn0V-XykKG6_H2byNxJQ0CLhT5rx0Lh1DGAAnje2EbNS7d4DpgyRu23gpQ75od9nr0ukull49Pa6AUe-zFNoWm3TQcJZ8Xq_7K-nNCeBXxxMZOGDlwrW56ASZgzyXsWPxjaBISo-I4jgZmMIKEFFZfTmloyLYSn6mgrL7gT9vhBm8bmIO2n1mfbqUBr_SydL6IxBf2YY6NJI9yiofYrwQA5LJmfM8q9rQgE9BbNzYSRtef16HhvSwQtdLpjs0rfcJQveD7Sd5IwBZvBT_qwBqUcb4idBSTTFThnG9DlUMowwK1ceLW2o0F6UgERmwQMo4w29j0z2eqdCU0mK1rIRnL1NiFONd7StGwkzuimyqLd3kOHDIM6TuQNsDwMtjX3QKKrc3A4UD3OjwgP04VVFSUFe_XLxleW3lnjlzVE6Ljez_lsGPZQU2E_aXQ5kTKnbr6-B0oVvWw15Lw6I-opJEKFMymB4LK3yJG629CPUw6eQW7zBW3VIVBrH85QS-RABnQngNYQ1rijg5RxOqgYB_6SDzQkoNAwpSMgKfITN_vqWJwiQpYh6rziizO33-F-soxk3Jf0iyCTLq3Cnqkgic1n4weJd3gmD1QBueiT-ytZFRB_e_6Kx2iDeXxRnHMRKiAfuYKImaLV7Jf51cCUFa9cbsLaTBa4HIbKuPAKHRUCEYkoAiuNDYUBT-Vcnb7u2VzikvA1tNO9DmSLZ-0iNZOccIB12gQlla2Ljk1KtP4Quc8JW-wItfm2rgG2EdaFyM_wNQ1FBjy6k8Z_JqmOgeZrVfa-AiNasJpCA_PgMxkyiFI68p7ksk-SEB2GP-DIjG79zwzJBpjlYsSCS8qFoEMi2lV7MltOO7fJSOilNuipBrrfduJUM9HLnp54OPK9GeL_5b5pE8Ft21wRHE8mKw4x7tdYMx2_2_dKWJOVpCQM2pQ-hv3UMDdfkYva236w-AZ9nO4pNZB7gCNyejcP74zsv9w1VzIANkaQd085vLRXbh_vtpycwp_9yT3MJTVS88GJaxX_Z39j58bIj9xRIxPsvRbtSVTrZWaoYbpnuhq8CPhZrTKfZ6_KriUyOOzp3GhSkeXNQJ6NyUStPcyHGP42VR6x3Dl_lLMwjFnyPMBUx0oOq0puqlD-QJZssxkPDf7-6VXJNXjmnt2H5p2GeCZaX_4LwvXeJXJRlYIMyRLUErtSoCmFlpiowzYjmWn8JPCCIrDzwIUJQCRJ8deJSm-_pZwYhs6x91QziiViiNK9KBsDmsOaBBHPfmqQgi1KHUvExK0-DMVrX4lykJCyQ69vU2RR__zPuTMSdurtEFmiXuDpJHg9BY3OwW6p09Ac4bj1IY7UU_cfENDxMFWCgkA9dCsnonjFxYsbhemuNakJ7MsnJcLf3Sh8D3TenpZyoS5oXK76cEg6wHUwT0WPJsTD3EpsQRAWAp-MLE2f2YKWWP3-swb3tDGoCPHW5fZkzxYQxXwbBV13c7iyWz1jkpAO5rsnU0a7J9JUY8WmBkHZwMgbHRoBTO_25ZigXgL41V_44oJELDDOx6qvYk62NbuQ25jAK0zJh5W54bmrxPuAZzHJySs2PorvvkL4SUiUbkQ0EJEzSUaEZsyM_QwolyMA-Yh21ieI94li0t8BGxcNepyq-DIA98a8-u_3SPSYW073RlJiVpBCHBTOj8yITGRfICtkjk3UKINjduVPV9GDArUuBJsVxB5grMyusUJPixyaC5zv2v8n6RGBOmGowYglehrfIOOuSDAFIRyZS21C02ovsaKcqJ1WqzDo8mrcYSWDfgT6pGgwVyNt1syeT27RcB5JtNLcxrV4qmZR8MXSIEpBwCF_21RUL3iVHxykcCWbJSpe7kQNt1hcxGs6q2Af6ttyd7IKjAnylukRguV0RwZBffGiJXWbiR-aCFg0BUbQQeO-AM8C3TWWnK6LKl4umVl3bq8bxZq-hh8cQgIPw-6FHD6K66uyOtG8l0_AhwzGsEx_VrA07LrjjQKSB0d5zmJkmesRkx-YoSiDW9R3zd_TdqYd4NMATHPd-_NBJwDXCUNadrLLlbM20kiLgqKEOZA4QCVhezgyrGDp1m8upbbQo_YVdc3HOmrcGRiigZZQSoz0Bzz4O-7p-ZqEn_Tlzd85K73jBc7T6PTn6ZTakb519Ok_Ef4olyZuAA_HJfiVO7FCmPfB5xbjceZD-Hz1V5euA5LbYz5t0HRJyyMQwHp1k508KW3Gn4emyOPIO889HVv_6ZRMNr8q5CcSvRuDVPRYjOSQBdRnFCH_tsrpUSHp8piWukbIeew86RCJ6G_E9ggycLawO5STrkG9OI-VOZUy0Ize9oHJa4kXzPiwDbzgIIo3DKCtOOP4_F1YDv7ulb74yyL1zpVN24w-TT-7YPiVOQlr09aYoEnuF4rpBoOTeyxYqyMLjNS8O3K2xPTjYpTlYD1UhAUJwad4N__2jHVtoGlK8UHkhJDPVKWLZjg8mt6PYfa6mxZF4TGAC-O-pQGJ92QX86wlcGOzKN-zFLhVg5Q_9dUlUhlXVSr_RJDx930G-7l2GdjLRkhSFKl_S4-g_aNKVdHmO6VJ-6bRfpLZCJ20AjLUeyebbtan-ZTjfmg347LH4-PLPHhOStH2pbPoLsEA8l4VeBXm5XSUDucJPAtzfnQVrTpGUg0EPikvFlbsv2gf-J36WcEEgu-UI6izeaErFh_G_tvjuFZlDXOwMTsulHxmgqTwmLaf3NBc7bzbiANMn7hJZK9suWmxDa1AOg9V9vO6x_K-01LN3n89P6jDQ8mX37sEtPdbWLpRVZP5rOio-5hWzmY-0TINq-wN_9Ii-x2A6VK1-EWsyQVz0UsX8JY-VRkFE9OgZmdVs1yDgloP_y7XIzl-CSL5Slqstuu3GRnoLswoCVlGr-3jFhvt1pt75Mvu_iZ22eV5F85af_LqM40nSDf_KFj3F0UM-SP7OiR8QpSszDRDIcD5qx3a7scVf6P_raWoARgr7zsXkuw3Ne31uxefX2fau03Da4cbSQxj57kni0juAWkX50QLDIkl3V-F499nHHZmA3OPWyoyuivepSfaT0QJYFAzJZSccdTATu8-tEj9N-eenxFe08nZtD86l9HSy8W0yAN0lpndy5ZGLBC4Xf7hfoKksEzO4GiClq8bXQrUDCWbaDqdCpQ9W6BVqB_EF-iATsPWwmA8NqWs8HElcsXTHV9Tb2C1POsqynsjkpi3ZFhp2QnXVg7u1qxspmtZMDD-eC2wQ8P9SRNxO_PS_A65Ci-BVwotGDo2S3PcZiX28wGskZcICU7b3SF7VhFP8XrBaCZE1MlfUKwonGzxCraTMWfxar3h0BTOdJYvAK3bpnG07rtsnIq3jhIw5gavjMbTENagw_Dhl4ndsJUjyzkOiSLQjbqToVD-4mysiuxq4N-rGL1frcGThH7wnJjyNXFNYXgM9Tya7IQ8Zqg5iY5tO3knGDY-wBw0Q2xtokUApP8tO-j0iIgkSGeHrQeNZ9LpQUMMEfKrzYgymwDsFxa3gMxtmrtyGy1SLXXuBj3790iSbsV2ACBClVBAfzu4maxBrU8AghpNfdpNWZcPqtnj0E-mpW-U7NAIv5bgWxsqn6D8Z6IXaKuXlidYYYzLyzbaq5XgEzUg73T8o8-cVl0u-XrdTcReLX2Iv981bmo0q6wCacFkdDsKoZFKG4SHEh6IcjE6LAti6B5oe5Huuu0hEjG4b0sfdqwBu8OnRt18tOKh0Dudv9EEc0TYbdcUf6y3sYd6TwXFsMva-th5uva0gwzyF3MlUHHa3GQ6z-fJVRLu4PuLXBAzzkcVa7AhErd4LLYl1uqOk8RMqEqRPMgBkEA1EFqz3ZBJzG9wMZybtNoRDbT3nb1fAsUzGSNItxG0JuHSj_H7cRdEHLE0bDIw_Zh4vNEQdD-jCTq6fiYJKbIzEQ4DhngOQF16ngTI53TLp1zudtwrlnfqGxiTJHDcdeVy1YnblY8id4y2dHi-RXhwRndOE9CJXBrxYawYuBPSpLsIGXjPnUJ3uIgJZGXa1hyxIeiJuS1Zom6DMG5i5K_VFtGf0jshz07b7fQriwbIewbIGMDD0rWfsczCArhF6K8Cqc-qOcT4if34XruFzdtHtR0RYxLCeMr0PRVSn3TVI3oBqpN-MCRZAxb7DjEA8K4zVgYhmS7sPwcchaWtL56vP9aSfWPIx-44PHS7DQir7MuOJgGYW0ISJZUmBsx0uoZJ9Li4wPl-B4wzDJwde6klcfRO7AVMK8HKmWDUguo0bBDGYJG3Rj5p4sYWZcxcHvFOaWc0t483YeWwOOehGwLdWAjwXzDS4-zeKJXXTMaw5OlErjPg5gezhmZaBNambASbjtYVsb6h1ro38AB6MJEhhkd4vGIrdiTw2_VT0XK3lCDd5grfPAbRUkrWghNTRXitWiS8eW5F-QsDleVxX6R5NWU7Xa9irNcl7-HYdSiwoXWVB7zzC1n33aQeqM3qdmsgun1RteCGeu2RAiWFVHU0p3_Tt0xNnZLXspHB7_3g9ptsrQ5CRPRN_XzSpMXHiUtuObR_uk41obxi0ExOAfhrWqvw4GQ2v_WXv5xu7IGS5y8aX9JT7XRuou5od9PE2c_CLLjiBGGLbd46oycIWVi_zwdFwG0PfIiuc3hlMjfAGBMf_hqCjk--PWHVep2uKOvxlhCJVih-4b0fR4GVqF-y4AN1QWSz_-hkUc8NSJhkf2NVKWAtm7VpiTpIZL8wcOULrGRe5AR0H7fUrc9HyCLMzYOLFVkcPx3H4OqjRVJXBryArIfl-W-_7Oagxgd-DofiMsqYyatf0Pb7k5Co4h-yA9IJDHqaZQi47VN-nYhIcVreH_-Z0zU4bcEfcHRVAU8hp96BtVtbIIzAw5FOxf-R-UBsHFSTCs7a-n_EWJBDlrO-anUGidlDyE4OL1SICIonFXJHhI8J8iMp5_jDUdLfSandOqhY1tm9MDdidLcidWremss44TywwZ8b4_x6KUKClxDre5RK7G2CYPNlyEikA4GoKcysCxb4U8OXs8pM4WmJEYDkvQXSf0vsGhhDcFPsWzCkP0LgA0TAZngJvXe9ix8vujWRLdOrRC-P14d9YJmJU1nZZnJifffR9tm0K1KBOJP9r-pHF83vkigHsbTAk74jdWFEPzPHjwxvgq9Zo7s-RJJk1eo2lMOvjHnVQptSczuNOMF2KclMEQ5O00oMY91Cm0uE6ylmtIzcKmJPyIieojLhJhnKI4Y1YUeZnn8S4qpe-VU7Irh9m1vZDhSfUBblnLpnsRH-TV9_-eS89Fl-MZtfMze-WO9mNaPaRTRZTvSEA059twGB7wDDNiIRBOeMKRJfN9W6gBzmaUGcCIaZw1lxsl2YOU7hHFKe-1eFPDlXrVi0P6Q4ZnBQojvKpekuZ70nuY3pviytJSO1S1QKbNtpEG9u5BrzYkdxNwtOF5pQpQREyTRj-RO9rpsoTUQoc85Uo_9tX2Jc-oEnNjhpXj_e0OG58tTI9hXua4Ub7Yj_VzM76hQPrAI_2iq0DRVwg6KbcTqpjPqWObqVtvp4HXLYh0ZYDJrqqxtatCAGGPwgDCPnhD-XFSSuIHCV5Cff484cQ1YQ0L7xo0g-lC2GZwzQVVFpPLqfXi6Ht_ZYCmMAxKQbNEmZKkV-5R8PI1Idtmt9_p2qYlBmU1pWKDKLqj5yOUOOvCpXv7L4dICvVTrzCoOdqMotO4PAytIPI5rVKYL0_WNjpY7HalvEg4uzgRjZ_z82VehU82n13Xh6Q460XEWkZjYDMB39imocrtz_Ic1otufCsP-0aY0MSZrKpqWY9HTMmsFIR2pDKaxcL2n6Vxquswnzh_nYmx9VKMh4MnbyboPw3eKCnMvXLPeH9S7RZXR6vQc_CEFwibDHmKwhrJuCxfiugqiytHyUNLRSPyTOjFtOjXGtj3xt8xEEEgkJUysuYRi_fhS3dCu0VtyvZsKciFzsROsYOaCTiycuypWY4byWUg25m1DOZQZ_pYinHi6Xjz-q1_Z4i_R0E5bLbKdIEp93_6BW-6vmXVeoKy7NBe37aI-CvTrBTJyC7CHatQrHV7SuMJ7xLZZXHhPFWUekWLEIvc9Zpf5epT3MWNy7qyq0-7jmIGwEi3ufstqj0iD9MKuoaiaslGxv3l9I3zVMJc5vjXpaglpFVl81742F7c687Hr-dQhFMxjMtmseJv9soiS0Yt6yQBXWXgSpwo1k9OxWkNio-t16PtoTMvvJaj8mxnmo6QFEp-ZjjvKdP12wtVf27UOjRrcyzkY2FFcFehUZ12YPmHphDFXKK7YrFyk4HDlg6EAh0ZpPA8ELCJXIKk_PkdHFDjvXO9fVcN1f89ebSQ9O4UvjshW4BmpNaaOi2J2rEHV2zWRd1Wit-twXAYlEomG2AxZQ9UEh5K8Lj4Xcy7n_qMku-zeDczUGDop8-pR_AqdexeYqL08Hxlf1R_pMyErXIHsWx65F1ddiJIWIgvcJQoT2L7VWrQcXQkWFbwXlt-NQteGHX6D4uFXGOarf4h-58TJ0ra6_wMkmWxbrOGVTny-UgFUtVFQSx556rzig5bW_2mZgVVVIb9LqGJxgHYrLaAHDx4qU6LlT6Q-ShhhboUJCMzT-6aVkxuvg6ZHMmCIvYRO3MLzgEtHzAJLnuhcrZN-UQVngQBo53GXHvJhP4Fy6zcTs_oJspJWk0srkg97mfaSzZuab3_-Ew3TEJWShWwh6GWFoOqiRdmab1pO8DcC_JNlKseIsXbzyeQ7Q7FRsrsSCv8IVLr6mGUSykWspg5dEUN9-00chJUzdtLUyHpxKUjA1gmXGFdkINP6ViBPDOCoaiQilICYx87sTKPPC7N7X0TvpNqG9QDTrQ6a61MpqkGKoeRqprphTkQDbr0G3VVPCFF0UYxy9oZg6iq77_3EQjEXZVIKVswKCyQshxSgqg1nR6ZuyyxWacj8Z-iDMlaiiZCy56g6FZfs9ezMvySVljGxqIFnl8mnWJ-q_8Id1xKzDF02sKEXxgeTsgLWIYcNT0SKa5t0VKnJQ3BTwRgApJJkVOcLpOz5oB6p3hba1oIuwmNO_nDMmJ_FIShiR8hwZCgo-WpU-RDxrF0xmwaZPb97NExO-l6NV7IH5ARLDrtPGGhBWz6PrTvN-UVPx_HtCznFi3tenL8CFPPzKek_USfmElOYNLUsfuaRO1-76et6nEacyMT6ENoUjHk2HKEhTTZ_dseCZfXzfh2mjRuX-oJXj842cUBWkRkQSqGmMhezk3v6cn_JCn0WjIs0npsHUkSzfvtL8t5yJB38BoWmL1uQAeOYHGrKTspF4fBUJrkDbEBihgLzYLY092ZL_P-CcBxhRaXnwKaor9fyS2i1u8sluXnH94kox_ydeHe79A1_dq4-VLUhqQCMBOeMPT1aHOc1GsG4Pg1EBaFD9tO2PXfLMi3icCIG8NrR0X9JvAIV066A7CB1Agt9tAEn3j69Pq3n7EnemfhRTcP5EeRl-jLL_Qw91nDBedPWdRaEwnprwq8IFkbIEkjPskZTpKMze4c8AbX0criX8psPRg0yU6PeEQ4liYKCDylZBI8PdapM8Flk5g_6gN2vdaBOvp97SOI_Ee-2D96ZOaZ4WMsEFND1zdayerIHsfB0NajgYoZ5pkuBy6DNW_R-Ma3SvIHE1fhw6kbKTLui-GOSZJBn3ZYxJLAJb5Vp4c-X7lWEquse5Eq9PRZY5q2fjQfrDJbrofdJloyx7oPeK-Czyxmt2K4glFlSwHqVW9erj63Q4zxXThIHlVgm0p_VH2RaldjhdvaOR_96gBkcZ9Xpgi1vYUGridpg3vaRLghoPwp36nNL-PM7RHAvpW2-sIeJYttjQ2YnmHi6cOM8xYKN6P7U_S2jIi5hiStoLxn-IR9oqdMGOpBZuhCt2GbpvriuwWPxJn2fuHCHUYH-6DWzq53ljjjhkF-OuqYfxgWPqTgSRypXxblZIW6FuuFNMRxV-Zt8scggbONj8Yq8HXk9N_nB99XST2lc1SNG4YXp90VjO_HyvkNQW_uXjXO_TrI32k90d4pKHVFuKLoaqqew88AdOwPw4VlPjK35cGQS7KJbAZCnbG88AqxOL1E_KlVAhcPD2u-ZOQ8Emyr4335PGtkcRD9yGFFe5O0SyMAMDlLFXal2Dv_c4tuUqOoidYxoQWViwD0csS-F3Acvv44XJNzW8YsJwXNtj8g9eh4Ahh72qRH0m01odtAad-z5fA4AlFnnfo712a7s2NgfjA66OdzgnS4uESpG5JxLi7UM6RRdc-A5pN6l1UjoPxPkK-ZMAxBphhEWDepoN2pkhkVmiTMbKWg82t188w8lcpjnscNuYpwHVrPaksf02_iW8LyFRzR4N_fizdikofXnSLyLL7kNuD2l-uQTkyZCurUTJOSBML9hKP58phBslHfzVLlPqUdizk4DPW2aKXc5KjQW3jhJO4_7va-FJqMjgDP6CuGBbecKIJblTVd8XyeG0AdUsSTZiM_dZ5PCxfjDa1x_9SPVpDxCScnQTTIWwj8BCvoJC9CXOt8zXB1KrffPtOUb2Eemo7lI4wSmOF7JkWYGD0nvya2bKhJf5xkPmefIFzu_iJb0XTxvKK9FtTesQHwwMc1bRuFcU40bdlnd2P9nuCsAvHyA3JO9GR_6H7vgsuWzA9sbY6ilKi7H1nhtGxvUtFI5xFE82rrJLJk3XDTl5WMbo1SsoXAmUc_nHs3h990jwNQBtDyGKVDTFuUnVsnHawcMC4gfys1mK8g9-Z8AAtK9Yg4zKIoNH8_tUUwak4EBxOHUzzjqB__HVIEOxkpo38prTFp0AMElGpLYRRAJuEQqqDtU8W1qPSrw9s2Qdcwm9e3rjlRM7jO5uU1XcFlKdHsxSJmkJHyMTGYAXroqcBfbw_NAwr_1-VI_D4D0GtuRcuspn6o2kM_jQIVBYUW4uS7tFizEr20w68xGnlB279cGUcYtntt4NpC_aiY7G6GCOsFZ98rQt5lRVOPfitCvvacEVWqfhMPXXGGFzwz-Zd-geLtEOJGA4DM8UlcH9VKX22CNWcxtWzp8QMB1F0QeXG23XDJRF9klgwkFuG7B_D-VzjEKluGxRktmWc-CNfPQiYyb1AE6hPK8I4EUvfZHmif9xOGzRvC1-zfZSaqxXti-wH0LZmkwdpuBmlQXcAe2BrNAp0YcTthxhvI6jqHagBMeshO_txFSqcRD_weGhpT_qxD__3-kOfHZTcMlnlLA437zNEIbGJNDA3mQDJeVMYjSB61SKoQsjzYW81roXYPPcKDQpJk49xA7-gj-7ICfiyWXxtIN05HXdFKOS2WuqfAIgHDXDxdRJgqpYQNI6Nh_GeCBOUZWPHeGKWvJYOKjU8Z-q5CTO8cYsuFz8c1rKknA-BTA7_P00HoOYYS7NryskkI_9rX0_Z_QxzWLb2Al0kqq3EU4P6qESDSsA1B_vOmrfmsDGCTh6uwccrobl19nIdPJuwdc16NyC90dv20RCdJNXIexUqCyHqrr0pbnLVQNvhGa9UDXpz-ysHfHN-oQmDHgR94iz-wUbwQp44vsSS5I2HD04Piiyb9T6uGBeSIPOqhhr9R2FagiDf7Y4bjvuvMSrr_O5YlcxNs3rChoOJIyAO1txIgdzNCIgEanrA6OJyxSyUBDhXyMs41uPkh8RPJAKLbglCKkDwPOjuWCvmYR-cbsk9sWOuKrEUQjmOewT9FgBV7_9eMtoLuYrlj6xC6AYfPrTbJ72UvQ940IrqsxfTcjLVjI2ge4w2wxKP2SFMOht8pT0CkO0fO_a2D0lbUpASNYhBo-gvhepRZ4PSeT90nyl8ZZIkOQQRTtaz1X0i3fif0TeSTSo_P_wWoLmEqOLiSdybg-B-wviIW9uE3yKKELxuIKqemb0fY4gMsgXAlMsjvSOsvHQkj1AzM1jONdZUrzOfKyncsgg8DakE1UR5s-CYFSYfKgUGnNS-J7jFM805zNFgiMQz4qAoaAmzsalXmeiu4R_Y1q8zaZZC0I7AufipC94zWw7F5zQ2uQJVt7R29S5nQhxVihBqUWls54stKn9PZxrGoivln9whz5r_MincCEyYAwsYonWsIA5n2Wmvvkquiy1arwRP1OUnVJM_GtrvHKv6p1dPW3hhZq_9Mu_6QAKu2JPaHnAQC6PToq7WybUbZ5WqvP6MdCAU3amw4Zf__EG5VVSdYSVTRfxvrnjYc1XwycUDSTAKLZAo59YLJpEfd3wbpElx0Lx0Pgq5jstc13bkN3_Z_yJP0uo2SUk3KyT1bv8Qw92xIjAVXNH0NYm2FrDb8uQ3c5XrE1O6qdhuBLJhgqtVvabsWRUF8P6il9cZjAixMyYqMWdAOi00F-IV3RuKYqy06bDP3cHgSUN3y1wtlPwNQ0kjoadvIgik8auqX4bklipDlhuzgpjRoKtWMo9OGe-F7UP_6LIRx9hcV3k7aSW5l1w89ObVCwwTXCt_qkab0TWFzlRNJu58J4OComtMHSDL6s2OzgP9gcSMkZ5f-V5DLNuT5J-7OGdrMW1l2Cok5N0Rv1gtSGIGibNEk6Mv41Z75UNJK3lwvkJn6fa6aPfG_gVl8VJwsOYm6a9AARdyr5utBZdjD_yysmlVieNHmM2Z1wj_1EbCKrL7VLbVUgcjjzze7_qvP1KL26Qkq0Utct8q-wjQhinm6Wnot3dNXzCXo_IstdKQ77dbLEDJ1juWJlryS_y6iiMnJrAAWjHGvJbkmMecxFeUcKD0RD3IjgoALlZn2Sf9PXffFTkqT-1slV-6OtCYPQVKMxKX0TfvMyoWOY3JErmfRSsq2Q4_1RxPFQfnLjnpIK3YyoxGA-z_2LeSax7ilgaiQZeRbQkcDn6nOYleLiZhb01UnATF76yUFpz1ctp2QHmnfJTjiC-UCXdOrOgH6-C6FebP9zXswKwzbKGBfEukOtyXKmaoVVg9YpujwXmftxZxqR0T8YZu_gz0pei8bIhKfhBClilR967BWSFvgB7iDEDLg7pOvH2Hd0ymSRZjUPTfS4lJV4KfvvVY9aY4Mc0zlERGJXnnRMCnR5QXj0zMh_dFkRP5sx_30IFK37aKD5RAtT1fm8tg5oAnQs5yLGQbyeyOPmwNYWVISSmIrXxNPXo7lLOm5lq9B3qpatX82xqmkYgCwHzfMJES-98L4ng4qT6iidnetzVF10uTRWQUu16mXDYZpTxf82JB86lSRSMWLXL1XPUsHOhDaFscbxzxRxVDqvMfmJlQgB_p0WnlCZ-tqqmN0DCkrs6wDHA4xLyV74LXB7EZFqkWRUCr4vuR5M2JFiN6KDBgkmBOimvLRl9I32kXXnSWx4lTNvdO1-oT_obWaUQqydrtXdclpL1vH-_9rljHjOmNZPDwJmrKM2FuJvYTSWGP8T9SqfQLjzXjztq5GJLIPvM3ij709a4s05Efa37TzS1F1Gmnnk1pwajqkt2aAw9KbryOEQDiptkb3dP6nD2DJYQq9Oi9Ej9UL3LVc1-u8_rHuRXJo_sKjuOYVnHD6AkyUOXI_D8sOQah17IHXeK7bh4MUWQnkNOpNQtAKLlNLc_zZfSSOKr_n_9roVqaSH6RHTNf2qYhz1RJTwyJl4ZziuuQ8Tdai_9GnsAfIlxWrMtMQGqnK1-JaLgssgvm8apy8shdJ3GyBEhWAIlScD4M1Dsl8GYJ3Up8XgsQDrAcLdOXqjzCM3v6LLPsNOtnlXZA4mY2x7tx10CPuLErNn3tciDM2_BEEtAxXGafV_pUBGIKS-Ied3x_sNakISl7JYXo8F78D6UPaVEwsQrR2HtwPDKe65AAslF3sZ0raCHKYQMnJlb9-gi8NfXAOEYJNnSkn4cCIPq-ZMTwZsF60iOAxKw-JLRHSWqC3EFeu_2gpNzugQLUkXR8vtCJbdIZylJDbAyRZbOVu8txv7d8C8pczYQ_cs_98i9hSLwEZwhDPj3devMcNRHve16amJ4YziL70lDDHXFlGKsIFm3qACXOt53MiJGv1ZD7w4hofjx_00cIlVqSa6zGglq1t9fcvutr1yb5pSUpBjHDd2l1sD5XdemacXtRzRTs_Mu1x29NHcz5jZT8ECBjmoQoh0647knVVVumse1U6F_ERIpRmn4E9C9nievODLG3J9vo-mlYTiZHm43AV2B2K-R69fsbaZ8NcHfwy1SIuaEZ82K73TWJ7SOASRRt933abAGmxuA5CCzDajiltPc-HNOwtOBV3gKIZOORGjMoYDyfeRwSIDlbTbUq8a_iKW3MW5yNt3o5Vyl5m9bJqfi3AbwCAHLDCBCLHENKRy71G7wBCZW21scH8gWqOaDa947bD9gydvU8VYGwTTf-fiQUfrv6rbCVh1M_bYYxfHRezsEt0uJH43DkuHiRL2scLxonSE8WLQYzFgbZxh4GwqJnI0WYqo1RK-rl_uTdF8SzX_VcoztOKxMctGEHEfG3JCtccT5vcbCccT9yeeLS1BoTlmcAyo9hE1PPRzuhBvyXozGzz2hTux6K2OxJ3ZbvGr6wpM2J5b5oqZwsjHArrjrbZhI2cUZXJ8zyqkbG8MGeJPqUAmMxLZ7ls4qqlgXZJUkGpZKKhgjAloXuYyl_oJNcrkWAAh2ictFB6d-w5smXL9ZHRoqIIOAcx8002-l9dV4awnWf1qdZhFXtqoaWnT_WaG5POp2gny0Ci034TnVXNZFor_rs9bE8KqG-o2YupvUAfcqx1JoPKAUJ2xT8WNeQn6qGRd-VULttGv_k8gdZeWaYePt89aHrxJON9Sxt4g-u8Wb5P90YPSv3_jUdtbouAO0Sw_D6ldmgVunNvQzc0xL881q-r1vumO8ITNfHagFaMGvcCteXVLSSpnqCnG6sY-I0svKRYAuhmzTCT2OL0txDhmJnYtrbtA4aARvzZJGlsnNzRrlzheCX-XzWwsybkQ4Vpmrr_C9utKiJ8k_1agixR-YW2qszckLw2dQrVDDOiaZTvcpQR-UkoP0qJ1Cj3dW5tpsELOqUT7HJpeN5T4C3JJkuYLnF7AE91-Bgos07iq8eefsv2KhKsar9M5eRYbiJlbUn26hauZN4SPlBCTzb8YPz6o9ElX_BmnBzUbnJzCBzX8MYR8PRo0SMSO-tpXv969NPp43BzX9vuZbu9fzu4-OBygm_YFw-vU7ekI2ZVOwDg7HxZVJdWvbeEz8l4Lyr_ESSiZGurbwU1nu836Y3M4lq_-CmdB4E6VtLU7yb3-yIkGiCul-sd05g97Yym9oD2Il4gbnIHHv_6lo6fBx7TMFNzUvDo3iY7OMbV6zA0enCM0SrcM59KO0P9TLcgEX09rO5yOGo-sLg1Oa2P1qbMZhh_S0wE-VKArrI0YzCp8qpuz701R4hOzOHRPf4GvIimmSsGnU5WLUoUZ7nsRewqISh0SkNLM_NEcJJgT6miDxZGV9ffYC51L2usUMKkFP8dAZrgzF5gu8cACO0uuTScC8TvSvFpUG8AxmfJd-eIQGitsSSXsDN_Qcd0elkBnqL7i3skC36oO1ssld-yb9Ic-BV1JytnAjtpR7t5UXJrQp_v1gNaKvl4_eARXOLSQn1QFaRsWBdrTBpvf1-WMv91ER2vrbfHlEZo__4x3RmI0StD-egTPgrr3_CbjVzn_h01IumM7gyattWCqT0dlkaOgE_Js0KSO9k0ex9a27TKSuMLEBBJgcIA6WqIw0srpdOBxdLLB_1QFv5cmmrDv18vRbW__czG7C5w10R5zXi6g1IG6vbiVnMJe7XyZvckRhSdHY3Q8bnCl3FL8BGCrpVFVrRlwxFf4oXPZ6mQhml9EYQXuQf9YqQz5gwdS3lKws3Qjt2ESZhtj2m5BKWKT-pfQXqg1Ds9FwpJwrGIKJEDTD2x9vgA5r7QCWa6sHyqWJr_l2j7MqQHViB6GyqIRO3UQzW_Zw4NVgIBA3HUkdM2e787djTSEghpnWnC1o5yXDY_juoPTchLwcllaqnDFZsAs_zubPBraZyFzQsUzPk34G9O41FLj5gQELS-hPQLPYQJusw7Gqb3578EgjU6S7kjyVEjqUryu1Vmi0DXEWbSzPCepFlZa7xEmIdqBDUz9e2CT4Vptx-oNaDpVnYROg2Kb6W3KiAf-1ao2V17s2q5z4bEfIXpAfSz2N_NT8hDGAeDKCeIONFb05R6pN_3FQGYzt8FshMndB0IM5JHdpiSeWZ4Ak0SXaEB-D4lizlCW2nG50RnqW4UjHghqU9mw-J3T8spaO8MWhdhpZorV8vo_zQWqXWOnZ9xfn2RBLNmsdjzsbbek9iL9h3kVyaNaWhJmUG_lQu3R-igaLlv6TFDI0R-ZAOia5SPZJncnmPJsiwKu-l4odmMW8YaK0_MCSYbKRWV4Q9G_RV8dhzKvVeYwAZmSWrGtLutaLFLhfVniW73LOkSl_GZfPa4oxrB1wtpEYBqVc-fesvE6LsmVgJYYisXlZf_RXNjp_fryKsaIg0fbNNgmFxL45FoEhiyrV6hRsB8-_sn45c6BhrUWGi4h5zVBZ9l1xEpVZ4VpvbkqttIgxRwW-KHU4-36RsFBr4xvmDcbWywX2dp7NhFbx776NnyUAs0i9UmHu6TgpkUp-s59jDi4Pib3UQx00jN7dX0YdzmoelozpKNxFBVejNGP4aafW4J8IBvcr28rsLQFwHHuvxcjjQCAQH7ZDG2GKDdyNFf3khrWhhcjkJHBn0bSNbW9i4VhHwO5oBCV2e08UXf6fceWlV8M6WKYcRMS1XJ8ReOXkhbNTberTg-7EHeK7VsbkXNCFW5Mp9M8zuC37-nEWSktG6s09dev9YBEVzZVCSmYfPIrKH5wZeAnEiZn12LO3xOM4hP5kX0PqlZfVKuxzyO9CEiHr3R8pyDKmJZjfhlWwRsZFNSuccO001TEvsIytDx17hCVglXoSxP02D1Z1ykaT4auYVhYzVBiALpdLF0jiNX1Ru4-FAfVqM1Ga0xECC_BlKlCAbSPP2gcSM4Kd68alwGDOPrOMvnFmqVVt5RllX8xPrGb2zn_jMa-zYf5_IbaYX-wYUM2_oNHARc6rs6Rz7qc6vf-Ja-uzCmZCDudaNaDWoDQujhwf9Qk1xFG_qtB-IZYO7duZ4TtAImL73fpgt5415ScKC02JuA_sHeGWLwMCiFpenOlXOTZUen9CCrtbnWO0Zjzufzfsw0xBZ7cSOLRzk6_Y6MYxWb8l7jAt05HB2LROaMmUhXhHie-aUN2_P9oGYc6nTSIis2pTDflBxq7ZKLwX7vzrPwjW3uSEe6RX64qOvCMlTfDxIZwriEJ9ILSOKtjAgyvKUHObptJOuVcPUt6TzKSMiJ16ZNvLAAjU5ec6ZV_txAS2tdO7olAh6O1SVz3C8ChIs4S61k3lSA2axiSBHL9xqkz5FlmjteHs88oYJtMqhOCd8Q7CFQa8R6oDDsKRmW8FdJqCodiuMpd4r8vSaWbHjXbP4KP-RgX-s2KbDNI5fSOw7CVayv_xmugMEfU9Ez_ajez8gdnPTxppVrnrxjULze8mywY50YjGWfNG6VJ0fDDErhHSdnPh1IRaAhmJkXnjjnJa6Z3lYrnstTblM8jmtnZw2Wvm6iFLrSgiYRCElbHnt9_3g3Jop8o88o6XE--VLPekJw5VyXgGCAMyOXWabzWF-jlLSyaM3SNp_5CRrDOZvzMnE82tvuiV7pYU5mAbqUA3kaouFd9s11A2Hlx50qg0Zw7afj5Fxd9tCbHFRnXzBvUwDY4axbGUJya5Ce7ugdr6GUCmAcv3ukKzsE5SDqKxr7iUuJIy-siZWYrN1NgryTVuxHJQjU0hHDq1iUd8WENKvGt5DV357IbnhV_S3K-nHVpcwOUm9L70r2DHFYBd-BGKGBuzCT-jDQzvG_C1Hj8qkCBjWw5tVXzqdqliLVURUMiPaUBnG6aAAg5BoWGgEG2s3BP6LIWCRYI0z-MUd0iWEeWGamrPmNh8Rl18Q8FpDECjJ8qwouj8ko-UGuY2uw9YfFogEUlk0p3IskbEPhY3VB5Arp2lqGpjGcHFytOWzLXwHu0onCE9Z_rsSYflpwAc8sJtcKzBoQ3tkYKNDx5lfJ5-siWtEdpobESzZZ8tKAFw1B779Ix_0aTc-zdV6tLcEEt-yRZwK_HxRWiQnml2MqQ64jx6a3D4c6l-VkWHSVH_1_BYlr3a2w2qtHOHstzmsdIddiEFY4u_vQf4soMoDwoZ8q1WoeDBzBu997HQOG9683Ulwa85y94LO1u0rVwcMPcjBOv0-oDAgA_5ZVcZDPvlVfp4yqv0k2A9NZh2mAcIW_ugAiYXJRIPB4w2jhODE5uYLMksXm34n3QwD0RHwC_GZd5X_TQRelcuiA9P0MQqUtHu_1BcdI62jhBN8TlmpahdeiRoHGhgFPIHLorwWjwjN1nBpPDRJq_WtT2DHAaKfWmE26NWUoYUGl_88JTQumhiIWdWlAO6GK-loY1HHgL_V7Hh7x6a-LzGlS4bcTT-oeK7pDdwZkmg51cYQFT31rDr6h_4Nb8QVA5SkUhZ1Bh_D7RKr_D9v8T9PpwcU1qFcO3kf2FEwlNduqaXl78sxEHTtO4gSA6N7q4rtOPs3rCMu3VfjfTdqUxsnKlAHDDNOp8Q-ooWchhfwcCBBNYGgPNHplT-PfJ8_vG3ccfmKlviBG2mA6J-MNxAGijlf1GjSStYMVVbmJCTsHvPU._fCyHBBy1s39UbK3URpLHQjVpyPUId0ZdyFRl3TAmU0"; - await agent.backup.restore(backupJWE); - const expected = JSON.parse(JSON.stringify(Fixtures.Backup.backupJson)); - expect(stubRestore).to.have.been.calledWith(expected); - }); + test("round trip integration", async () => { + // empty db of linksecret + (store as any).cleanup(); + sandbox.stub(pluto, "backup").resolves(backupFixture.json); + const spyRestore = sandbox.spy(pluto, "restore"); - test("round trip integration", async () => { - // empty db of linksecret - (store as any).cleanup(); - sandbox.stub(pluto, "backup").resolves(Fixtures.Backup.backupJson); - const spyRestore = sandbox.spy(pluto, "restore"); + const jwe = await agent.backup.createJWE(backupFixture.options); + await agent.backup.restore(jwe, backupFixture.options.key); - const jwe = await agent.backup.createJWE(); - await agent.backup.restore(jwe); + expect(jwe).to.be.a("string"); - expect(jwe).to.be.a("string"); + // running SERDE to remove nil values, which will happen during backup/restore + const expected = JSON.parse(JSON.stringify(backupFixture.json)); + expect(spyRestore).to.have.been.calledWith(expected); + }); - // running SERDE to remove nil values, which will happen during backup/restore - const expected = JSON.parse(JSON.stringify(Fixtures.Backup.backupJson)); - expect(spyRestore).to.have.been.calledWith(expected); }); }); diff --git a/tests/fixtures/backup.ts b/tests/fixtures/backup.ts index cc6898f71..36c6ea5ff 100644 --- a/tests/fixtures/backup.ts +++ b/tests/fixtures/backup.ts @@ -1,10 +1,12 @@ import { base64url } from "multiformats/bases/base64"; -import { AnonCredsCredential, JWTCredential, Secp256k1PrivateKey } from "../../src"; -import { Backup, DID, LinkSecret, Mediator, Message } from "../../src/domain"; +import { AnonCredsCredential, Apollo, Domain, JWTCredential, Secp256k1PrivateKey } from "../../src"; +import { LinkSecret, Mediator, Message } from "../../src/domain"; import { credentialPayloadEncoded } from "./credentials/jwt"; import { credential as anonCredential } from "./credentials/anoncreds"; import { peerDID4, peerDID5 } from "./dids"; +import { Schema } from "../../src/domain/backup"; +import { BackupOptions } from '../../src/edge-agent/Agent.Backup' export const credentialJWT = JWTCredential.fromJWS(credentialPayloadEncoded); export const credentialAnoncreds = new AnonCredsCredential(anonCredential); @@ -16,7 +18,15 @@ export const mediator: Mediator = { mediatorDID: targetDID, routingDID: hostDID, }; +const apollo = new Apollo() export const secpPrivateKey = Secp256k1PrivateKey.from.String("LLW8vWvliLTHsW5UYox5VGtps4sOrrE_rY0HdqHAwN0", "base64"); +const masterKeyExample = apollo.createPrivateKey({ + type: Domain.KeyTypes.Curve25519, + curve: Domain.Curve.X25519, + seed: `45bf23e8d566035db46ae0904fab4fdf9ad9ebe8601ef85c6426262a653502f7386f94dced7a0f7837525996232d7b879f8c347fef94966d56914d6d2f3c1411`, + derivationPath: "m/0'/0'/0'" +}); + export const peerDIDKeys = [secpPrivateKey]; export const linkSecret = new LinkSecret("bGluazEyMw"); export const message = Message.fromJson(`{ @@ -33,45 +43,185 @@ export const message = Message.fromJson(`{ "direction": 1 }`); -export const backupJson: Backup.Schema = { - credentials: [ - { - recovery_id: 'jwt', - data: Buffer.from(base64url.baseEncode(Buffer.from(credentialPayloadEncoded))).toString(), - }, - { - recovery_id: "anoncred", - data: Buffer.from(base64url.baseEncode(Buffer.from(credentialAnoncreds.toStorable().credentialData))).toString(), +export const backups : { title: string, json: Schema, options: BackupOptions, jwe: string }[] = [ + { + title: 'Version 0.0.1 Simple', + jwe: 'eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYXB2IjoiVk5BTmhuZFl6dmdXdkVhRjlZNHllNVNYRXJCLXZSZkRTRjhfX0o2ZlVUTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiTmZKWW1YUXlsbGZWbzVlRHNCWC1XVmFEbHVOLWE3aG1BZmZNWW84OXpoQSJ9fQ.Uearko7zbcV71JW7bwVnzN4fmlCLeuSdgNmyH2beIhREVF2n5Z6LAO2wyPXTROuy5m0RKAjDkF_E8NejFsP_kb5iMsgjX1HU.hTxmr6GRzczZ3RQOxk2gPg.FW_XCdd9LKrHpZBkT_XR5KE1J6nw77FrYzS2a-U25VKSD-RUHpOU7Qb-WrFvm59UsqH203lgCN64Wg5Drb1Da2td8pSAkFp8s_H_DRYJgsnm1hv0c90XTd93Yavhnh0uXqy1TKazk8l7I9nOi6F0MN_ePVfQpse7LZ8FtxlsnDf9aBZaURmXIAVuMRrmqyv-PNNEUuBWieT8ISzexuHWdZZcs4I-7ukWqxaFw7xkoYqDCci1BgGseFXw1pPyvZ8YxM4UWKf7viQ6fNvI5v36TMTr2WUsG3aise6OvNSC6kmLzIuDsPhH1K-OsBPCLvPD1DVJ5Is_dWCxX_EgPr4FbDL6zPoaRUDTKFFPRaW_wJzTQEDb16B_HlWldvOILos-Txyb35oqFaaU9CKlJ3Ni55gGrNh6sbyDWJor2wruxtA1uJ5SMwbxmKhYc_F9oTcVVCccA39uWhAJdOePoLrokzji9dCOs7mAE0fVxEyzn7FsJ9sIfBWZZ-Ce7UP0iMS8OOh98-lrQN7aYpxN3ZMzkGZXyRVD8mWgE4v7nctfttWHO-fxlsCu5viTaUf0TG1AiUwmROglT8-CAU0TtdZATZ2iHl3_Ii67aQ-Q3pWvHB7Xnv4jm9v5Nh0R9R4zeTeewQT0yJIjK-yMqyOvJvDvA7NO4ZDDobkV0yZ4vsYjgIAUlSw_R99IS7oJe3rFDHlEUPrxVhiLz0lnPVD1t1hH7Pgf48Uq5W6VTt_HX4vp3eC4LFCmeNZmbqNt1vGivm_IU_TFsK1poh4cJc4pT0kj_KS8SLrHno2umxwEGnU3u7xymROVWT8j_YuCSx5aUKPZqvfkuxAwgArdETQ6CsYaSXQ4z_3MrihRppZIoA-OfFZJDw1WbaQ6ybwVd4eqizBbhMV_DYcSgJbQlj3WDhJ6YcrxK4Nv7MAzPU4qtc8ZiFYX4iZ-keKGk8V3mftHMR7s1dBUj9hvM8-v0b0L6oq8YKPNlv90zss6fNS03o0z9kBWg3cfmCpa7m1oIcuzK-5l6TbelOaJ2p_5IQFiLQ9UHgTp8jqrsXCF9v4preDrJQhQ7xwDU_UpKjrd8O6J93hTUOyJKkXyTs0qmhwA-4L1_HjLc8jmh2y_jqiltNDWmE2ctllSc0GvQbBb7iKNap8aqOat4nsrew6iU-C8DG4v2yGcZhXXWer_Z-sc1-8aV8UQKSWIlbAICebCVyJjbIWy7Hx7kcMMhkY8uTScLGPyuaJwrt8QDLzfBlxeZam_lJIxfikE4jMnsLoQtluIuxnuGHVbx6bApLPdQg5c2mTinK6_tvnmMiU5t7y9eGVtFxcCExFcKO6Y3escjsPqGcLpZmGiY0IWWPo1RpUvJ34bhZ3wvJQFflm7wNtLz-gcKWhmn3zqfcbuL9NtwQIEAI-feoyLFhl5NEXrbRQ4Fr9EIzzpsBQ9Zkzwk2gF0_abL_QTRvtO5Kt51XJXpybSXxcU0BIaU-smKccT40GWiIGdrCG1WM7Evr6gsGTi-IjNbD03tZUS2TyqOD1Nmx5NG1Oo8iCeucUvHp9NRwVW1BNbiHUGG889evW2pB_Zyy6HxzdEUwnBzTpIz-58ybdsjoU85lKZ9-Fg4Ig1-kHPcXHDzpzydtiWsdDWhDmFNNJACHuuLhSZP5Bs2CMJNkPh4uoTf-pb2NqgBH8-BENwliUJuMN434RXE-dYNSspD6xZpxF6IFr9feTXA7m4AmMFm4jl6jMb0sUXfr4GQABCBR1xdYYituJNMmKyAxJouEJM0bfuTLOFVkUXUrxmbvKBintD4rpgIRFVC8YI26xl2aGa1kyrlL54-3jbAB7wNQr0-PedTSO5cJ-KdR8x4IPIPbwANL9nZS9ZJ_Xt0jg3veKwaqfs-wuZbPie8zmEoMoXVQLvfMgdhrsfh5SBfftD02k4NtDyJQFQzpFQ0m9kWhpg0v_mLjX1TGfoUx6Iz7FFXLpRsjFtiqEHfv9LEj8-qqZVOZa6TeKwACBbXIPtUuyO09soWD542q19a_X--gKiaBQSlv9sqbFl20KM7zLoX5SvHEhnhJ4JqSZHh1AEU4WXj7kyBwtWxqO6DhG8RRsVo7xpV428uQPBEEMO-0oTeGTaCaBz8uSfaP9CmQRExcFpTIH7xiex6usLgiQGGoVWVFaYp2PDrk3QrpnCWVr8gbFSix5y7sjt15Z4Q6ejyIiCSTceUPnGvuq7EE2qiC8bGYHJ2H__UXoOSqUw3LI2KMqGhY10RghD3_7K-vrJ0dyBNJeIHcKjxj8qRv65IcRzv6DN8j0Sj-odJeJ_a2q4RyThnndbA_thNy-ujFnKPL7LPuwRX5htStH0qmhy6tFPRg3B18wVs4edLk4y1tlhL8WYys-y3rZOMew9wACe9ibty5NqpcvcOysOeXn3lc-WUNQntdUmE0vXC59pPyRZ_U4teeJqvTAniuosAtaW47Sjv-opp1B6rwtl0hK9rash8uo12yrsTb2QKdd6YmiX05mIgHebT8vJK7Jdi6rLC7JOWHWaAG9YMRFb_s_eeSPnLfef5P5XVyZ6D8Yf4RY0z_Cn2vT59Nj3q5f72ppcuqGzCI9YE4DZ9uHZByZV13glEPSs1oPFfmtW9qF3YoFXZ8KBh9srglJnqB87QXcxuj94a1KsKEN_4eKgCcTLLuG3Ap6i0hWq4JBuDdQaIBmba9rgrf7wVAcqUadt9xEqMtS3_awxZK4-v2N3I9ehZe1ZQ1xrnNOo4L7tvezD7ypH6G-3LqQYz6doWLuyc4ietvBES1C81-MvWEb47iWS0NrxHqAuCogwE5LBzXMaMbrZ8I3WrmCNP9HT2oNCeE2k14w3NLw19bvRCgOLIUvdYEe1ACHvPH2QSyw_lT8IRQzPQ1fpQv808Xd-lvnK915oiQ1PcBhgA1yTfAyoJXF3ShcseX1Aeur6GB5NQ76hqj3vh1kVWmMsX8Lsh2Onti895IbWw1UKqqnA-S1qUHFzO3_O21gBwYMAgAUfY7HbBMOpwbWHG7BXgippv73ZEjcP8I-VPfkpDA1AF4EDmtVSEwyTgfrAJDeTJqOwEM122Qe1X5E4DbFBdKKEYwFr-QmMOtXvlUtNtRefWSiDpA5axTSkkxGyHmVWkVb0C_P_qFLkCCqEC7FL2n6pyNQotrPVAUutdgYBKNJTtvOWXurUk67BQSvLr3VvTviYaAMv81Ntoi3yOSn-UYCCin-0q-2ZkJfyPLOTtg4kHM3sxfzYyxVeeWGgZxPjoBYAtf-PrABqfEG1ioFpdtsDACCXIw7Ntw8VIsR-ApApGIMf9eUEoaIfJ2dQhZXcTzkHIfJzdRrWcSEisGY2vOByBJZZFoDGhRNmeLXnezE4Vho1Sh0e5ID4cTh7Q0Lw1t3xRrrWzqm9mgEopjK4c2qo8eC1hYT9_mdv7ZPxV4ziRU8T6O6WnKMEymEO5WPTRxDEV_6Zie1SMgWn4Kcejc3QUVgo916YhBaGatS7B7G50WfrntjT2Xw4EajqjgwoTVA550_Mx5qySWYtQEPOE59iuzIGZ8URKki0ljDLbkzv-SpSWIV6FjLfW3QifnPjHy__IUTbelkVq4cbtrPMEOJn0qPXaBbFztSuRCLr8z9CR6WiXGA1m0lhVm7FlmB0UWGsY6GKKfwhPZpXAozf4PnGfbx5QxcgnsozkvkhGQ1DH84W1O-hS1FWxT7mgGgoq4gy2MnwuEieKvU0_RgcVuNLuHaEAyT88VueaYdCwHzc3PXt9yvXrLpF7_ezfgLX0JE3gCmy3xw4Uu1gww47HKsN5Wpu_OYzqiw4bmxTq-RkYH9oESJ8eedpwwjokkOyoOPbWMUw0rrb1bFhjT9l_lsPg6k4KJCSXyc1EFyK6PGI_zyMv21Yf91epxNfCaFVPDrodCbPbZSUelt6dWxUd_lNgR1efGScOQg2JXXixbQW4MRzK3-oWjT_TD7JPIj0cPyVYo2pStILRzw0Ag66ort725n-Iz-zjQ1tuYmqZFMyjY8PfYQAntXxMXoGxXK-IJIGSPmMK0lMRdxnOgO02dYxpd4tyfgtsF2wC-SNFp1dKkVsyhwj1SEN_YlpAWpLUDHLfG2mCuSUVwuDsx_mX8esebQn-_MIcuVtVesm2p2IQ-RBHaiJyeZGzqh4ik40kPkUbHo7jjLYCRFQQg3hZkocx5f8CbxGTR1wtIGBOh0HtXTL6sQ7xfmaYyBeDcmpmB02ujpHTfqCPjlktmtP7oI2DYokcopUroWW9abG0mO8hWwIMJmrwtWcSOtu4pgQj1D17SWf0ngLuRzKhcJcLbIuPXRspJ1xrK5AVL7onNyz0vm-p3at86RQkaZPQ4KWp5NjIW_21sWVzRKfEc1hbJRmV2qykOf4mWIdaHtDeVSM1JTmdXho4ti5v9v3QNCZzdPlldWbUmB0GID5ogIOMcPBBDqw8-zbM0jJR4xOBwqOl2ziCplqFTspaID3Cbc3QR2bgweAMGNgEHYFA6Xte4X4ATbKYBo7YU6a45tNTNqCGbjvsGypxPLm_syPtYR7jD6F7oMXhPBH541N-VcpM72WM4JCt5iFovo_OHVn99eadQ3pUBGurb4npMjP193LUcpdRlwz641l0Rcbxxgokl4CvSLpT8c-I_7o9-0Mce20z-fGST8ezvImGmadVKTdg9IHGxllG9Pcgm3gCcC7bf05n-muVHFNRvnrfA1s7eJcBTutbrXr4CpY6p-YJUBM1iNYuf-zTNMkeOZprYkE9c2OsqZGmkBTEDcUjuDIC9Ja58oc84RT2gaewMUUOqqMOrq4I_K7z5irJ4SWJOIm0ITeepluuIg3lV0StiDTAqBUQuHew3luu6C91L5gsRJ5fBNj_kb1cpPeO2pTCuXdduiXMCObpgLyEbPlecoV1sxwxMM2f0NnMA2MG1vIHAWeW_M58L_GfNpxgjbc-Rh_kpaaFhL-3w4bfavcnqqvminPxRr4d_spHPeoVwoNJUTRCFh7XWKN9Qz2V6EDDx_g94lZoU8f2aDCba6s33aIdXhAUF_cNhmM2eCqRCik_hDUD0uC1BRsxK5nAqX9QbakoyserIQAJZqAoIIenQeVvrPfU1sb_NCRfIrE0c2ONQJADHNiVCMgSkxUU4ySsvipZscP_EgBXxule8DarSSsFSVKOTlYKlSRHvpoaPCRvdorn7O9Unh4fMgz0Q996dY3js2o0HmGYXMtlVLRtZWWZc0SSz2drXQO21dfcSkk2OArXziOMkptgHf6VM7uq-4CmkshaeVq4Ea23QGQmTJeuG3l8LGEK4g7LDBND84k9WO9O0NW1CQG2D1EoMQduSUuiFBZOdC4KR2BWAxqem-9jJGFemqrybSGp01i9fpdFijlPKERyXdyApv2uIkqE-Mvh4nlN0n8lcN12leb8o51WY6TqtNxasnWT_moibbGe4cLtYboPs04hAAfp8s7uM-ScK5fpMvglG_W3_fD789jLqHEiFs4mPac8aFWnq7NkV09xUTRbO4DTw-h-MwZ80z-5g_9iGpy6RToy19RBo2cjIMSAP-2Sb7j_3UgzMSBEtUStj7C-TYwq3zPairM2jiaaW8y26IlTYKDT7bfyzTL03Ak-wYEOL7Rjq_raVlmFYTUZYaCnxVXTKxoK7_wdvi_ivCp66MbnBl_r_2N9r1WTl696yr-ScKH1qo67sk28l6QSe9YazLqOhkWzftUSUEooKSsiyrpAe3hSY0mF2tGKw8wuOyxvW3gcdRJtbcm504qVLYZZBtGT9hqAZSoYpHbdjEY6swIe6lMq7cLuezXdWeUuYMKBgHvX2su5PlgTnFSh2fwSMJ0bJQk6UC9OOSgAmcYB7vYVNhQLZUzhMozbyuLmVXo1cxED_hmvJFPVoXvGk2LND_spsie5Z7JcvWhJxA4M7nyKWi2I8XZdST1-Ja_EZQ8uFQb4a3SyuhESfP4GJIEHjkr5JnUnSSs3WmKOQP7dDeyFrri9lodJkKEjy41WJGgcr9qspV_5VZxXJ9MTbY7TNhyo0QdcZ3Wd1dek7r9IqlVO1tDgh-cShWQ0gm2leLca6eLwYJBq1-sLJuFwSnip1R47HY4bjhtX5DSlZUJbfkY-eh58zGOl2d1nkWf-RO8a9XRmGSnM_wyAgE_clY1asneWWWGAQH-ah5bXYB2UXjIERMwomCn3IIGUu_iGJ208o22JtEvzzSlIsdbwOgZWFEKwhJwVWjcFCcB1HrtLq0Ow1Fn2CFj-KxhWXMKDYjlnHL80Jaz8zL-F7CduEnw62mVEv96WKACpW5QnSGXgMi5J7Q7LpbzdU88eIATI50UZip5Lg75nlOkAPiiXNQgsQtSRjxqBq8HUeMJAhtoi3BH2Wl43grkOcSzlPYNeLMS5r1cF1Liwn3bkowiNfISzx4H7-qj812AmjO2y5tX3mWj2IuQ4OYJii3HLJNUqyAv2aauRSlUrc8NQwARXywNYpxo38BRZgw3otmm4k-0IoV6ad9QQE8ngZ2gLbWi34hr6_2v0i9rrYtj3tcjdyF7kKiLTPej-ZETFyqbm3dmIw3hQtdTWEpsrl5ULmzHKEl01EAKvurfDlrhe4FtA64AsNOiVSIXm1YrGIcYmxdAOEvvG2ZUziBtKj-lrgsAH-Dwiqg7Bn-ZEMPAmFVXXH1Xd5ejosNAAn1gtXrk3RXyunLI3bl7q1ynTmG9lgpgCqU63Yn3irqRsewMJjY7yQygolnK9l97IpNxDYBPZ_9V3i43_zBtMglIaj6PwaKj8bM0GvmQzcDKVue-iymZdtwFg0k60O5tYP36M5Rp9k9Gbu2wfgC9M-4wpHhjeWwy05qqt5rzA_zia0DuHihYzRMmbcUWQssvYVLCphC2cd_Q14706aVpKCZYdzlCfB86tHgIa0ZtuFs78yWzdV4yX3e43UnjJih0h75XmpxJQXdLInzYw4VrIgMpsv7Z2Qb3s1irALZrFn83Cz3dK7CUIR3alGM0qrsihHW6Y_jtxRkP8vMzzKkV0qAykNin5Fl7gslmAqJN9Q00zzWsa_p47Fb2u31YiMk_oy-Mcpw9Oq-uVKr2l46WuLeaL6qJbZfIhqe8e0PV5ibJvOY7CGMYV59V_-uzlqAUrL2IPBjHE-kvnUU3KIgT13XLOAEJYbwn8Y34WMz6eWhEJCom_uZb9BcgcK5Ya6XeyxtdxDS_Ya1-LW-tx_TACAC7wYlLCngirSWi9P1dK8TEXNeyTgY1wXwBHLUJ5y7I7WE_eVkAikRQlU38mjptvCNL5TiE9oDc4nYSk7D_uUkJZUAHuRc-NhEUOdyB9XuyIMBvWt2QVDOknUVVu0psh6OLBKkudkBeO-MKgSAl0TXFus28hLhQTorZ7gMlhBE1z59a84dV6aAip_nHsSWgoYvQjP-br0JIeSqLaJ_qH-urmNjWRl7MbJh5vUv5gwn6WFGeCPO8q4Q7gds_HjvitNhNBaMu4G-lmYNJE-NmyVdSaHoe7RpScFWabn0PrkS0H2mOYRBUBo3zWzVWZcxWixCvJN6l4gShAt9xD8v_Te6w3VXjVYuUoJ7Mk6X-zsBXGHNsN5iMkGPTLvbJeB20Ndi3exlh_uGxPpt8K9kX9IPP9PnIx2fNrxmj3m1vUB-fOcMgmJvikbkOvAQks_mJiePUy_Sj6VSAtgeZHLSBxYSG6a4stp71Dw6iiWPcgO7VlNWXrVL4KchSMJ_aYJr99B3ilE1uirc53kJ-Ugxx9jY7YK1yx-j7zfYhISut-8lbdrxValerLFjiNOowSj4IFEqqFsJ3OV9RzacrgkbNCCm2hhdmzvpd2Ig5J7aiLJh5TPnFy_glqn9IFMjiN1n7ODvLzisgxJN3XGqvm_Qz-_sTcxA7Yn_obdDY4COjr-xNqkxgiEvDTPI7jC9B9IHsWB1YP7P9gG_0p2IQhAi-jv_PvJCwHIDbt9H0gA8uFdCylW12hIixBdntUDzzChVrTo9jXBaqNN9kJZ6VAdcnqm-ta_IEhW24tMAjKiQ59KkBy8dcxc0yDkvSbeKywBFfR_LdB7ALlbRw-h1Ubzhw3xdzudEiZx9EQjPn3RUB-STPtMWKOt5n43ti1zSoQdoz3ozpSv4eq-9pJdwgOd7mMI50-XFEDyZvh9rtixvlnmkE6YSXcqDhagUwJTW1lt6Rh3-aCeERTsi-SpG8ikinAv6BGM9eRBHW8WZlFPahMb4PgFeEVdx1-8bRic3buBeAqpyNFR7I9UMlXD2_9vynq3GKxia_oaX1_mZuqQfAhWyVAqMCXtFDs0OPp6cO4S8zektGSUUIl37ivYLdVZHDqZ8eaKGflLcs8faCpO8YAdxwuyp_P-Vj00WMq7eASkPBvjRFdOziiahvoVPV0JcHqVEkkeejuyBxkxOSOTrHH_C5vXMdGZfUwG8oa-25BfxWShEifsR5Y-ziAhPf6HvQHLHwMJURWi1p2uNTNQUZgK3Gb6ZeXHaghxN6cp7N7sG31QOOud3jJbzJ78AFWIyoZ58GFzi1jc_dFA4LGWmRE9k7EiSHXKfuNRMX5L9YFjzjW1vMy4d9kZpuBdDZkGmBP6Qgg8lUO88otIyC8_eGr50wCm7IL6cxhPM5sMdV55vIkFcXveZmO-gtK6VDvJUuZRyYHw1Dd9oXC9O00gUDehoZ-cNKe1ZsLwJta-ccHYozWJBE7nn2WeRCKFmh0V1GVr2i5ljkDCtQeotHFnNFflRFb_BqCUN9CTFnAFJTzaWkaLutyOdAp5TQZsNOF31RziT85BSmHHLCwr-Lkz4GGnQ-3lUYJJ15zCCJrOGUaPjkqE3uxhUOFomt9rTwPWPeIVKgATvxHa6ppCyYHQqz8L9VELopXfjt0fdCWqDLJzS52zg_VFllYurE10a6SqxcbQ_hrInCAenran7oBT89igzlze_RWYhz4yC5dXduLuLSQ1irNIG4y_3mkahFHP7tJqnSh-Jzc-ZwyzIQ90H8hJAsAM6ZR6fzyAEKXkGFWAKtZGYLKvagz-UXKMnex5XPCRRT_GsxxaSw-sJsn59EDV1kqLIn4-aStqDgB8TaikZkHKFCa6IqOxXtwHDdQT1QKIiYsSXQejnF4YTkDYcUz3EUrb2Dt2gpFYFcrCjwY8wYBLVGNc-EOhBvv3RGhn0y5B2fgnLXA0mhBbjypCRU-YdYZOd9wHCykyuRAx2ww20EK1R5LTgQskI8ODWh2a9otumInsQ-8sO9dJVx_G9O0mvX9frGupU3Rg5z-K-bTs_bt-CuL0JdZrl7mYIrfvl3JMfvDHRYu8KjsK5P5kvA_ebYLYfu1gNLDHRu2oHsJjs8ebrgjsqPQ2YYibUK1rTHVrS-xVaAbYJ9BfusZBFH0TwYhLSFrQS9bLwUe8NCVSG8nZz0HqNOibTQMWUSVwAeci0RrBkdNW_ZMPq9MPWzB3j2PEU0-GVrdiOwTQBeQKgd1WCXTCx2ByLlM54idTZ9JauZL6U3SE83oATlWgK9SZPbOw1H_itPdDtsscVVqNLP6k1MhHIdf5lpd1TDCIEWZYv0flVBJJYLP4MRvePyHTH5109emySXyKWHhX5AlJyw4-UHRolx9AX56y--QPrcTcj7XjvLbDimRfoFaCN3LKwnb7CrEvD4pmBdx9xKWXSlsqfyMVQfwsBBdtalxD02qlkbAZV6TBstrmoaysjE9OKkdwypSR5FYF_GGHYyUYr62hnH18wuS9RBoyh1Hd1eJMiQ83sRpgDfhUPsDebqUyRUrHu5_hHBQFWAu4yzHT2Py5joUNGQwJ2FeUWhUZVhH1JUyCH8wrq_V6UjsYFG5KTDJQXwUKIvokvFr3f3INS98pbdNJHtc-ff5jJAXRdJn-pcBglXYnXsB7rwwXLmW9GQUtFReAIRWPvXyXd3Z-qHq3QSdhMY3VE1pg3yJpXKgYITnD0ODE76fb0KUu8XDVSs4z7WP4wjECeUC-MWjzYMMFYTaiZDurjA_kRsSa0mYXomKmifn2kQnJjgp6Ja9D4TINvq-rbT5PFXhghZ4eiLq2YJtttmzaiydmmtlSa3iPA_YXP84umliS5TdzJr58LQGr_G5zCRNnqPnOtnkpOZTDtZ5Zk0hmAj_3NQuYgyceqhP5B-E371IXGg0IVUQqOhlhCRe1kCpy3jxYEg-jL5QsBFAktmBSEEF-Ne9sACIx0Gj9MhH4XikpQ0S80vcuU2KV9_4Sb8Y-ss2EhJ9pCfji5bFfn-eyXHcSFDJyOONuV-yQrlqgqtDPK1ZiKSWXHmeE7aVkE9Y6b2KnRq1Ryfn1-QzaebSWdEcs-hV5OQP9LbvKoPIdYgGb-7g4O2lwP8c1-le-sS1991jhfnrXmEIRj73nuYzKis2pwcWacD7FTnA2QaZyPPr-wA7n_k_QQoPM2KqsXME0b2G1F4BrdUVgHEnJt1ih3HCfdJ03nu-U2lkupBlS-J1cCSjR3WW1dCb7ziktVuIX5sYyUzqSFlyba23Rct-f_wAyp4T9exNXYL-dBPC_-lE5e7sJz1nvDm6DqrYNEW3KjtBVTn9cM8Sj1_4juKKgQJ4EJ3MYTtjjtZOoaRfrFdnvcJ-dVOChJW76nKbgQ84MtkXDXXGPXWzDfHUEIHiaNNSmFgHBvpAGU_ByZ2lxXbL38Cg35UBd--7agU-n6Dvj1D7NaWMJfit07hpCaq_FUaIreymRR95JKCR5QuKk3TGKjpITo-toq9X7tJAD-aQ0HU86JPOb7aVZe0lCl41M47qf-xvfiN02jw3dro-zbSozYpw8nedz9juXb_jCTOc8XaGMWRNCJUR4VlU5Saq4hZQp-jjU9FuJokvur9_kCzMbmJu3RarGjfco6JsqeERLBceBqRnYuUjkt-Q_71OqwdYAYgyQXB-GKsltM_2CyF6Akhcrn-P4hjrxfECtgSRijnAc70jb1IhPoxWyqzX-ad0g7MHGuIBf1gEnPIU_BesZ5OhTOSbeVcD2OwnSYcUibTM4zx2zulEsVvyUOOgPpZqYU5-trM8GYj-VOkqay0hex5WaZO53xkCNbzutzOwV3Spes1U2SECvsj5Y1te3f_8tetDLcfKDal497rE4g467_4gWjDDk0AN1_GdETMzZ2tnpCtmwrak_YCMiX1ad9KWU9rwQTl1FvmZF9E0H_Oo-w2ji65bw9gWcqQfCMI4z4aVg-62hY0MwjY1gSa_sxY-sXU0vCDL5fnewjHI3lPJ1HGw9AI3PyvkSK7SQm4h63-_geYfEVesFrniYFJeycDDkmsrxJhK9bUI9v1Qz7NS45QZCt8BibOjSmthxXu8GC6kkT2OVIoN90_ml8GX8iixRqpawefNG394aV6WOnBpUDkg92hcp2HZUV9bM_opGsww86iRzSKqY3qDXdGC89lD2Eqplc9CnzUAH8g_zwfbHDx2NEIjbVdtqkfMnkhN0AfVRVAV3tITKeOnhMhb1JhmmvyS1ic4PxDOhBJZrXkUlOMRsi9canUWTqYFHL3xGnEw0QlCYOE9Kve7-jcRQixiQCLFWSUgONFkc8W1BD7orUS8dQq-LG5w8A1y6HvQhC-3VsudsqhmqrXUWhI06JdhwPmbvfZgYw-nN4mW1nl4RFEdAbXpf3M2Py9094H7XSUjgqRSkLoi7yJTp3-L9hpcStIymSW-V-KQ_frWzDN8UQDyhWcFJfb_v6dJivvT3-RYTFMywXRjyOb4R2__4nq8QGvxcga7mYXLote-E12ODdWOKnajTD-BzpsFHOoe3VgI_LYEr7Hco7CHnSnRyF0c31wJixMfiWB3EDNbazEuWQWsy5F3tyccX8pOU9-BYGnkb5pbqPE0BSRAiV7zc4_8EnTuFjrWvVO3clOIPi_RLy_pVr6eCf5YAkrdCoEkHogYO94tLQ27ZJy96oGAgfMgvLgpKKDabJ7XDcuokR3UH6T-kV4h8W8E5hBZRjjIRu7xm6_JGLNBPqjDT6zdo_0dAndDqxVrLU29y_a7nzQJhmJFbPR_aM0zeANtl5Ai68NgfT9p1s8bgGHATqp8KgAyL063ysBp3-tw2jvtbO4dLqRrqQ6uk2RdWp22Fj2NL9jyx-vYomfv4zxRCd-HVobSbGIaqSZRUwQpWiPQQsRKbUC1QopCupLBgOWmxBSYFREJRoJcXpShPur7ZK3798JXkcp4Cbs8aop_P46FGEnlVTqNfCeBNWAJ538aJ-18r4VuLlUNh8DjF76CpI1zDqLnIRjVT-QAlZNL7bE0qzfl-E7fzDXeSLPwoY71eoFM0itQ7RHwQQ_gxPKaCJUj3VfO2OFfTf6HuWJ0rwt_hJp2hJL_QOeLPbwHyUr9R1UkJmTFwJMKaQqsaiGTMlDKEhdsdfnV5EN5aPR2JbMsTVb99hDwcXLh_tOFQi-qAfXRmFbvp8xneMXp6dkkx8UvBlM2whdFB0Mn_MfiHPPMUHMR45cx8lAcQ4bRVy9n1SIQmAQnEe4WGyyQGmLFmeMnEhQsrDtlLbTOuJHdbgL4vPebvixJEPkgIVrc6TVY7rnQKb_rlSTywbxvQpLOO-WUYhR3nVfRzdLrMJXzBHHUHAWFVbEKkgOIyvgf2_kl9Gqi8utHPwdNiTC6CtIrvCjEvtGXkUvOt6QAuvNQMhbusrFJQqMpQc7t_03JUJ97JfzQYuT4kFSPxv1n1R7_ETCBxx3hC_pWEbBL20v9gOBuP50blVNSvvGviYonac0NT7_W0U8urukKABMWvmeC4ZoxBA7gngDkLSenpUj0iQf8lHItWQbhUrYhGSbidEuj-4IlVV6Ki58c5WbE6m-fgV3aWb6rem8M4gKo4KhNYBI8KOmsSb8eHnvH6fJtiPXkwkFu1FEC8T4fx4kX6yiLVIHEl3gZiXnyYz3uhahXiYqp7PNOkWC7u6yuuGqhin2RZWR3PnTEL1HtZ2QgdgNYDwsbw0zsnr8pcikqNrFsSNjL4AbAZWovklRngXc98V2GUTIrbAALKzJl-w_2sL081ejHGRlZxPmvaZQpuvqGAHvhYMvGifr3bYJecjwF44PoD2ZhDn7jiLcKLSI9xFmY1KkNHkax4PlksnkHihMbEwDh3A3bCINBqNl0PxHfGrkihqIYFepTn1qQjozSxbX-sS6jnZbZSQRXIpS41tP3ZCQcJbhYfhgBBsrvUvAnGnWsdSsTBLRsXUATIo6O9wQFhK1ZLlLw2RwnpbLdoN2Is-jL6d9b976n8oRKCVYgYZUt3drKrY6K3_48pQzFhpXMik5Lm9Vfsj4LUDmmE6yk5CA2a-ee_c_6N6HYRKlbpUH9qrrqDiIx_XPzxFruw4txne25CKrSP4TLb42h0H1HF10jzaqCnZxqCl7Tr4cWJ3DryT5ajs9RHahyDRrCMjj096sL4PCGwVaPybcnztIxqiIGWuHKZCvi5O1lMyq1q5pZNq1rvzop16hgte9EXREiq6gxpZVItNsNNH6CCiEbVFUq9EXB79JUDH6mnTuDstebvZayIENV8C5C7-T1GLetYjsljSCLOWhKpuPNp2DJbtW9Q6aphml7ZGzZZWMyXbbQfz7G-_O9AVgyIl1VpT_pixw0zhaGHm3WaQPEHpPFMpvyKV8qycXQW1iLV5NKn2XyJ439WIA1eow2f1ORNyo5SiKll5hbhdxaMdA0RdJ1s9x-09gTw-6TW9BV8aLWcDiH15F7OMDd9HQZrg4LjqFd7hQbYR3usAJn5gdF_GCzUdxgJxPcd2Nb9s9Kjf2j70xQKud_pwNTK9JpX8meD4CshJD8JKyNkxdAMZVlyY0uKP5rcSHsLo57TbwX0VDhNZs8azMtn5DeBfizDmbxXsW3_BNG7MRjfEVaV0wFIi7N5RqIO_1whA2sG3qz4P5PlQp1o1ZAvr5dikw6Gs0cDOsK4BuGbES7x5z7_VvAQOtJ1JvFEPk18kZdakKtjV-9pIfAm3MD7oIE73NPAy-Dbs4G9U_RbFj2cKJG8z3nCq5gk4J3Dhxe7YIkvyahAnWh3y8QTWU9M9IqSEB1rYRSyriLxKm48eNZAExbrUx5y91qMiwELHKngWMBvZhr1QDKdxE79ucrxdxO-qG4zUvGjx2CIRw8tdHHi4OYQ20S_g6o-VFe37_WtAOyqczkXgWnNwDHP21d4Fk4mCPUnumMYg8T6g3cFp7ST6jrwgUlBOPJvv2xlhgDFnGWFu4JFx3SPao5ZhsG1xgmobgiYLrbl7DEs2e6Sz1IU1T4nqQOVdL7UiRJd8S5mkGQuLlvemkufKBuR4UghRDLenv-n7gApTqJ6FYzQMkvX39Cw7cZA626eQf2K1-GlG2lK4mrhaFOf6lkSOiG0ZyW4QUE3Zq7excyCVGhx99l2PFxZlA6diOXulzzL9lmCdROt92gGtzE-AWsRr2h6VyfE9t8ksVs2Qfz8ugMiuB3BaQk7eosldQlwWLQMV_ekJJFFchepIxcTgUpmgGrpToyt7WyeuxqjM8v3uewYFXY_wp7ZJq8HgIBrEwtyYj9JQuRceFIAv9pWHOm34dndGLavmtR6RSqmZic4G_SN5zzHuQqayF0HIDprcZgmL2Tlt69tO9CaykbAyZxlfSOtIKE00LoyjKvwPvyGjfhmtiTG0jPYEY-nFw3kLj1G5tqn6Hrfsil5P-Bfj2vSbytQKmYT0AhmQuMCeKAkr9YIkg2tSoQO7_vCIWX7jRo6gg4ig2OIGDalS2UwVUuWAQ_xa6-lu2r9A5SVxKFuIJ_BRCEPptrlUUvDAUOQJm0Fsnw5pY3fR-ZSEDZXdwiX01SXnWYHCzXQWAMH1330pUdyrkG3oLDvrlhzFT2vXjsGisejHv_yuxdpOB_agFubTciQg30BhhHINF-Z7CKVGwlArb5kSkQR1oLBbaS_02ZgIuCDYmVd8V3kbqBPDQx6FWPgzE7osrEvNSt-GoOreJJIJcsz4VudATefpIaYt50Jsqo3r7ct9czR-PEQmIhCoTD7e6_bTz3WJUxUkhRQ_crQqqRLGGrKzYtIpZVoRwd9BznMYiC4H05oRdMevvHh2U2S_2G69ZEp1eaOIVyYMKX24XZ_AFplU3hohXpswSPUKpBW4XbtM0YBdlK39TnSbHFLg4QpEF-1ilzqHoKuXHQpeXBj_L_RfnEywUxFK15cBUIPXIbxtyqj1TQirQRfKy86tksR46HI-7LDNToeWh1F3kqHIWRUzE63YIdYqjSQeeu_huVhC5LGYwHk0Eo8W2_iT9uASUdZxEG77t8z_00c_b-O2h4dGKbqz870Tui1elKerFDp0vESEDI_0BXAYcG2c7dKs7RyFppEji-XEp-gZUCWQVCJ5NcPJOp_StzhQgAjoPr2RSL5rutZKo2-5POlcD2Bo56ErsCPTSnKV7F7o6ZIy8dA1kFzex4qF-rhTiI2aybtnhxzUdt3LUbWc4M2BfKJ8qXEjgsNNqnOUzVFM8bfKGyHck8YZKM84hJ6fXx2PgUfswUoUl2_UBImiSd1RvcpUW8mrr3WeRSoQdls_9K6YGrmCM-EJ9cEjxa9eSff879zVjK_50vuGAypqJV9zzKuOuXY2rkGkJl-e1qnuWlZbOnhwmuxeQpdjPrKWkpUMf7LhCJhYj5AM6lAF2hx-Uy100u2S6QccWJfUbZLtF8tT7voHg8QcIXjQsUFCtfS9ICuJ8VPD88AL7B9UXy2ZCf6hrg-5B9fRYhMSzoJIdA62h5jJMwZuGfjsg08qPOHbQJC6CJ-xpa_6Wh-Df1obLzIDlwSlGZh6JYB5FAMrdMLhIHSCLtqUwCqyEjgVP_rJbCbIIg31t0EAHxYjXXw7LIw8y0mOPqcxeN07iNK1-haU7xAx1lXrBTjhL-ebz0QNFFlUJxsU90_Onsim7hsvRbeC94O2vwDkOnsQ0jhFHj3fae4L_iuQa7Cqv6Wc-9phs78NTekEhbfpb-DB_KPpcuw9pc4NrldTG0ywl0SUusxXxPd8M-snBJekyLVRw8xblnd3GPfFssKaEBHuhS8sCSngHvz86iEWVYuDk2yuW4GCf4uC0Y18AjMgOR_f87K0e0c5oSqRwUnONrPuOcwbO1TUI59AxXpGsnHDhM1J5qkf-b8Qfhv_TPSPlEmEVgg9_4WxRV1DGJxlN2lOsmJlMyA-9y___kMpJXxuaucrK97mYcSSZa_gWJNoPXI2gSY_0lCXwe7bL3fVCm1wDrhe0LZES6mBq1MR4MyaZgO7baWYswPsllBnsLWrVqjpV28ZfXya-ifyhDYbfX3gfbVXBfw1j8Fe_dFhdNXMV6jkys3eouNwnVbU1g5ADpvXMrrQk-VuqSlZ4w7nK8S_h9GPfp7yo3XhLZOZzCANrc29CTrFSbJ4wYITFhpu4C-06eUqp1BlcUfEck48V7NB_NyR7boqJ2ED-wCEiRrq0f5kFKV6igjOyKVjBxUl95_0dj4LMHoiGNA_649spqBaFs-ouw7CGzRiQ3aOSmyuasb3GAt-57ZhOhiHKrmjg5bXthcwVRZLcDECXtuYVj8Bj72A18LRKlRhxyvwKWVzqWXsTGTHp9COTFS-27DN4IPpxas_dd580kWNA_gmjE4ZgNualAH6zBb59JlUCyI85w6x7Mz3elzfS28-uQBJ4NGM20XwdnJpTGnJeJfFfALBxhqWSsIEEkpiTK4eq4M91kNyiH6K-YwqDQQX2bzitdUzsx_9VwmfmRT0CzOwh6gWp0e7GSNcJKn7WtIg6W_8DyoHP3-m4IYEqYDqsTiWh3x03-JqPM3D3E2strUkPH8vbJ4xFrPSs60BLRJAq26lm3YJvhyZ-yChS6wRBO3BAzc06I8VjZuetRkacatH7MEwZeK_vFg91F76XzKK9ZCsTyg7aUUbhh9wkvDFgoONfqIEETOG8jgKyP459Xxv8Rkhyzis-XOle7CHR6dSxFRojyDHzM1OMx9rLndvyaBPMLSgyHacN9IzjSIbv2UgQmsrb4o9CzXmd8IVcYXnaQlQplgJQKHkTU-kXY2ctHZfsXXtIsPXxPZqGFLsvcfQcw1nvh9eQNzfh8B_FxTYvguUML8A51I6iGI-fc2jSoDpKgJMMiEV1Smqb8GHyuOfR1sNwq2EE3SK4mPAcr9PAXF-rinw2IG4804J7UKJaK5oSupciUKyy2r7PyMvCnrS0mRR1aAQVGL5E37s2yelCfkuPr9pYFRt_3RqJ1eXksykD5N3jsn6d-W-4ojxQ2W1Lbprk442aCsszODNTOFPk-vjRAH4FeeDjxeBPh0llPTQY3mpj_HfkEUiB89DT9IG-j8XdY0gwE49pBV_IsZjMTFclcm_QswEpBEtZ520owh0DW-xvdNtvJzi8af1gNGG3lan446kFoIvdWly_vzIznwPVf7IVHFNnDNPUU_dVbItiLOmi35Tm3x7RVRk3L7xozZhRvEZxYhu08SoKNXIPUwpxyrzTI229WagR39ADri67M4YBEaW_pyPE--ZS6oosboz8BJ4az0FxMe38vevSfg.pjlApOi02mWUvaeYsvyKO4MmoY97vdv9ifK_pxfzgQc', + options: {}, + json: { + credentials: [ + { + recovery_id: 'jwt', + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialPayloadEncoded))).toString(), + }, + { + recovery_id: "anoncred", + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialAnoncreds.toStorable().credentialData))).toString(), + } + ], + dids: [ + { did: hostDID.toString(), alias: undefined }, + { did: targetDID.toString(), alias: undefined } + ], + did_pairs: [ + { + alias: pairAlias, + holder: hostDID.toString(), + recipient: targetDID.toString() + } + ], + keys: [ + { + recovery_id: secpPrivateKey.recoveryId, + key: 'eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJrVXFTZTM2SXFFaElueXhzd2tCR21hYjlLRFBXRURFdFlEU0RQTGhSLUlnIiwieSI6IkRKOWxoTDZwVW1FQlJYM3RubzdUQUVBMmJuR0hKRVpjQUd6YkNRZ0NXSnciLCJkIjoiTExXOHZXdmxpTFRIc1c1VVlveDVWR3RwczRzT3JyRV9yWTBIZHFIQXdOMCJ9', + index: undefined, + did: hostDID.toString() + } + ], + mediators: [ + { + holder_did: mediator.hostDID.toString(), + mediator_did: mediator.mediatorDID.toString(), + routing_did: mediator.routingDID.toString(), + } + ], + messages: [ + "eyJpZCI6IjMwZmIyNTU1LWM5YjgtNDExZC1hOTY2LTg0ZmMwOGJiMWVmZCIsImJvZHkiOnsiY29udGVudCI6IlRlc3QgTWVzc2FnZSJ9LCJwaXVyaSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvYmFzaWNtZXNzYWdlLzIuMC9tZXNzYWdlIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNieXVFOTVXQUpZaFFhcEhSVnVSbTZLTEdiYjc5ZjVVWGlhWVRBWXBVYlpnZC5WejZNa2s0U1l6TER0WkpudVJOMlZKNGtuWmN1SlU0bzVDYjlVVURodkF0YVVaQ3g3LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1ScFpEcHdaV1Z5T2pJdVJYbzJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUzVXZWpaTmEyaG9NV1UxUTBWWldYRTJTa0pWWTFSYU5rTndNbkpoYmtOWFVuSjJOMWxoZUROTVpUUk9OVGxTTm1Sa0xsTmxlVW93U1dwdmFWcEhNR2xNUTBwNlNXcHdOMGx1Vm5saFUwazJTVzFvTUdSSVFucFBhVGgyWXpKc01FeFlRbmxoV0U1MFRGY3hiRnBIYkdoa1J6bDVURzFHTUZsWGVHaGpTRXB3WXpJd2RXRlhPR2xNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREU1TGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwd04wbHVWbmxoVTBrMlNXNWtlbU42YjNaTU0wNXdaRU14ZDJOdGJIcGlVekYwV2xkU2NGbFlVblpqYVRWb1pFZEdjMWxZUW5saFdFNTBURzFzZGt3elpIcEphWGRwV1ZOSk5sZDVTbXRoVjFKcVlqSXhkRXd6V1hsSmJERTVabEVpTENKeUlqcGJYU3dpWVNJNlcxMTlmUSIsInRvIjoiZGlkOnBlZXI6Mi5FejZMU21Zalk1Y25ISkVEM1J6YkJrb1F1RzVLd3Q5dEM3WHIyNVlCeFRmQ2FoTm9MLlZ6Nk1rdDVCNVR3d2NVaHFTVFZNWFUzV3B6cHhSTXF6WEd5VTkxeUNpZHZ2b1BvTVYuU2V5SnlJanBiWFN3aWN5STZJbVJwWkRwd1pXVnlPakl1UlhvMlRGTm5hSGRUUlRRek4zZHVSRVV4Y0hReldEWm9Wa1JWVVhwVGFuTkllbWx1Y0ZneldFWjJUV3BTUVcwM2VTNVdlalpOYTJob01XVTFRMFZaV1hFMlNrSlZZMVJhTmtOd01uSmhia05YVW5KMk4xbGhlRE5NWlRST05UbFNObVJrTGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwdmFXRklVakJqU0UwMlRIazVlbUZZVVhSalNFcHdZekl3ZEdKWFZtdGhWMFl3WWpOSmRWbFlVbWhpUjBaM1kyMXNlbUpUTlhCaWVVbHpTVzVKYVU5c2RHUk1RMHBvU1dwd1lrbHRVbkJhUjA1MllsY3dkbVJxU1dsWVdEQWlMQ0poSWpwYlhTd2lkQ0k2SW1SdEluMCIsImNyZWF0ZWRUaW1lIjoxNzA5NjMzOTczNDM4LCJleHBpcmVzVGltZSI6MTcwOTYzNDA1OTgzOCwiYXR0YWNobWVudHMiOltdLCJhY2siOltdLCJkaXJlY3Rpb24iOjEsImV4dHJhSGVhZGVycyI6e319" + ], + link_secret: linkSecret.secret, + version: '0.0.1' } - ], - dids: [ - { did: hostDID.toString(), alias: undefined }, - { did: targetDID.toString(), alias: undefined } - ], - did_pairs: [ - { - alias: pairAlias, - holder: hostDID.toString(), - recipient: targetDID.toString() + }, + { + title: 'Version 0.0.1 Compressed', + jwe: 'eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYXB2IjoiVk5BTmhuZFl6dmdXdkVhRjlZNHllNVNYRXJCLXZSZkRTRjhfX0o2ZlVUTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4Ijoibzczbjl3d3ZxS0VjMXhrQUxuUm82STdpeUN5cEE5VS1uRkdNSW5wSWprSSJ9fQ.1n2EmfAxoC8QobOk0JUn69f74GVLPMi1LEK7ffeXjsqONC89BHfH4yiDS1D2eFKYiOhJxsg07W49QYOD81Ee7biPXk1NYwIk.WvWKn5Bnz5wpk6SQec7ZDQ.K8A5_fUtvjPHzQq4bRWnSAIr6WO9pUJXuTEE8LBFps-3ZP4_WDFOCkQMeHVWE4hO9vMHUi-IrnNNJLjQj_NZoMj36mENioq76O1qEeOuNK_q6pIC139Ub3H7pX4nrBB0RRKA4FAeKwfobvlzibcBBU8fAgR9G_mIocrCc5V1NhV1ve2jLzlL3Cg0kt38_joBa1Tp7f0-Yvajep-f11fl_3KJVigU0IMal-ZGuZOEfXWh1n5AciWBUu1FkkVnrok23_PAs8Hg09DKYG3_kZ5JKGLwOLeO4-BxJd2uw5uUu8Ln4Mflj8Ud3g4Qmba797aq0sV2O2FY7VV1jEmQW9x0dgAzCgjLNp-MKvgg-BmhXwyCBI9E55b1Fun1BXEhTSm27kcu8xriiLrHC-KiR4CbSzge52UviyZPtVgtEFXW70xnadQ1ouFdkNpP6N6QiAuphwAJlt686fCQW8TzaultXLtRswCIJf7VxKaM4mvlk6yEn1uTILYeXBZV60hSvdpYN_13HtukJGp_73_OtwuJcgDrdBqQu9pWcxd1Dh6ETkDLxuqEP4z4HDfHariYjmiB2fx5Qv_llNiPf8OX7MrNKfcuwLr5x9_-3AwtTsV0aj9MU0d_uNYNKpSKN1sgBJbxuiBKD3gQD_HvPDnXCAZSHkRjxyED_UdYmR8c4RiCO5vlLLxVhsUIfRK6T8U5fydcxXv5J0uRGrLr27b-0SO_NnWaxTbXkD8D1LhbYlCcfxQje7rLonG8H6PxqEkv_Sd1xpgGzylu9TnZSZuUCnosBedR4hZRTVpcEbit5-HVaKiOyF__kiXB2siP_HLpxRlCwLeseWba6Qx8iUyRSmgxMC5PQG5S4W9ECV0PGSUq-vrdP4PyPbjXy4w6JiK-s8vwpQTL7_6ubFwIgaTQFibNvkE3tvpgjfkwvgnm4bwghs1onx9P51DTNF2R2PkguTq-W7mQwMKdywdXrOiWcEBUsiKL4X-R0V50V7AVoFx0-Vc-Nc-ecrw5JKbg2EX-60zyFPqdAtZCZAs83PSW_grFBRdHVYH0ewzT-3Zx5PU5oGM26kCVQL4ReVl0n0d5Kf3BKodsKUl4_doo2BpzUN3dSpq3TWFLmPUmKIDNh2gV-_jZF376w_uy7NIoEGWf7t1DHEcVyaZ22w2vMZSCkAMIUDiPc0zQ27O0AUAlwNeGOHgpBnz111611vvKqxxRLev1JNZz-Qnoh1GENOgZw1B0ZLRMZavmiSiHqlHaypcdbMdTfOC1K-GoesUCfkjxVWZl8iC_5FKw-aU5a0py8AU3dZgm5DivE26PB-okKXRwJXv1vdyYjIL-1yWG09AdR29jaaHQylsFAU1Tl_bZsCFk0x6Nz_PvhG-8NnpOfSmFsGD7O-pqUZRjU1YV6LGhmfa2glRsTyGlXI0wRsOOMujRSkSvDO6jrIkbZHS1jGND4HOjGGVd6f5Ti7SLSKvSuzfWBqvlSd67mhAgB-Od87tnWvmSkCi_9GoYeUR9wX2Ww4nKSSmGs8EkC49RxlhR8d6uocswLiGATmtJ9cTPKqUmICwxKIuO7-90mUUPCWLHuLgaz_ts2xvs2FSmzaMcq50K0eVqMigBPJld-vXmEQVtXcsc3bG-CNEfaH2XBuiXZKVOKYnu4rm2YPiHToiZutHmpz6rRVlpc0FJIjlr9XBUXOlnpuOqyUdO2ddXbLXYpwwbwMRjR-LsvYqKEztqodLPCA7-Swu_rGHd7mVdVR6gpToWGd7ecU1e-uqRf5Hy-mU5sbLJcwkDX9_inq70IKAEe_5EK6edJWltVGl0IXL2i5-RSwNyINQFpNifggrTlQyX2XPp2cjbVhNh_kzbXD1o2OyUqNPyO-FtVaTeXArEOfdsoaGjPdomQdPHJ8oPM3GKBVVnqxX7FmXPNn0v5pggLql9Ss2WWeE8DZt09V3nMIF52Su9DqJjzx_El9fV1VKukcQQid_x61ZT6pyo3FBCnqKdH_n4z-4rPvvX14MQOEP_6YlABgQYGo7SlED30JMlqLZlKMhCY39UIMUUuix3zFipyn5GzP8G2MsXnqt9gQU8stRH94SgFzX_i8IYieKy5RAlznIqYENMcLyKrJYVBwpBgGlMl9_N3qLfoumn5rQOhZJDKro3a5UFaDzRV3HnoZynyipKp1zhfaDdLbWFw4Up4Sf6brfsEb24DK58Kco1D2WR3hEeasFOosTqnkEBZ7mvr-52m3R8XY4qC-fX8Fpw6-1ue7hs31EpBoeqbhCkZE92hIDsxX1szbKHn0q5G5yz3TUGgWDEoxQqvimTAH2ZaWTM1sIzezTKCR8z621V-yTADP6p22sJSK6oSCT1pR9nOwmUEKb8nb2qN9QnDTEE4dX3m-x6i1xXI7ZwFsAGoLVjHnN05XGcRDGkW7W19oDiIKDop2O08k0wnBkwNj12TWFQzx79oGhxX9PTuJvXgRRTeB-lqxD-EuoVko-Bdc5tz5RPBwOTkEVpl5I2mdleuXY-nHa5X26C5EkISAY6K9Big8K7uQF519LL2iq_yv5apVpaRburv1f8_6uSaExHIjFRKq7dwlqsiqImJlZl1wnjmgFfViiLsP50caCJqOPhfkCv8-TkFtVg3dsT_T2lEq1zwqVMO5kaCGUGcOKwnhOzpIXuH7y1BOqkaDtHlUU5O2KbvJCrsUxKvt7VzuLQGYmCm_SVD5tFMeSdwegUsyvHY1EM-tDyeoA1xhv__VZ6OgqrSPGmcabwoUl6jSsl7gN25wMxCauUzsY_5RMeeMS6D8yV5ssIrHyXqOpr3KGq7w5-0uDqAelA-Zn9kn4t4aGPzM3jL5sj_RCz5v5RlzatsQqPf2ELPrWDWs4LbT7kNyFcjSMFN-cBQgzZ7YmAGOnizviGhDCM0PpLZqDu8jVNIXPBfCPQh-_RqiPY0n1F7sJaHzfcIUZEsxmPjZBFBaSRcpZHByPE-X8qy6bobioN51ziN8c4gVADr-dxA3DpkZ0Lf6k7qu4gHyLLtMMFxnJIqun8oHgrtgEIYqyk4K6DZ6fye8RexQYfxiw_wH_TgjWkcZI6DL24R-trqS4ZHIcxg8boXfRg0DXhPCxoQGZ2hg32syeUfmOux7b4mH1pdh4t5qhjLT_vLKx7kxqSUyfEx-I6_yKai89gjop1M--BHq52rJ0vVi7eH724wJQFkzyw9kRRlMJ9ZKw2s-Y3rCJHHudmYY3p9l17cSMCLIfMtfA8EPf5Pn_dKb0d0wBrJt1CJp-w3jxkgfuuWZnmdGGae5nOM_a5lQjcvBn6rrTqgjJ2HLMexT-lOR4jxyRTSL6VWPlAXWPd68ZrO5xBoO9GhQW8F9OhIssF6EAK1uQwyTPTTPfkjJwdITc5ifcSaFpaOr8OoABVwKtZpMCexyqK34-BOeGwHuAc2shGHq4yIiG0UKrf8RmPZMyWTpYho5VrI6HUGxFdVqzOjl_FWAnbblmnZnsoVvNFRDMcsVdgW75kKOEPfRUUkh59KRLJvnlpGSTVb5CQR3e4DiFFzKn5YPTDbsrDgI-RcawM9D0HXk5ZbzuY0Kuxc_8aahlX1eNHHp7yvkPLXMxXNq030tTd_3yt5Z3lIRzHIy5qYY0F3EuW-8kPtrKDHk5xockc2DZXvfCJ8zdVCzLFvotp-R6eJChppw1NujekroPRTlO_VV3Ko4NvxuTYll-ebSYq0C7X1-B6T7O3O9r8z3bMHlKDQbmJR4raUHYC7j3oUCp488oXniG88L1M4UrYQRkCJWsiXmgm3m5GW3OV0w-W87bF7akQqU0ACFLI0r3VaB6TefhzVAMDSWS-E_mSQCvBqRbC47nTI3Nivi6jOc3e9FDr6F2M8jwlpDrVP5iLR0byO3bbXfSpnJCEhsyA721n_Y7TfR8BqOgwXVAZEhtVvfF_i4HPssRQ0IOJJeARiI_5YpYz7E1z65iJ2w0uHRgNFlKhI16NAo1kiujXF-kzKLtptKUQxXNu7ljijh_CAnjyW_tevvdw2oPu58n2FPCAHRb99XqfTfLw_wQeqnQZlmEYhlTKbP-GNGpl5S1ib7BcMcTNxOGTeeQ7yzl9gGutr77a_fa_bWWuXh1OnQ4jAm_tgxvQIkWf27WBw5BSvXt9nqPYkZAH5S7HzmHiq-A0UGUahH7uYkvqcQ7VbGSOlvTFIjwi200R0XNWvNrS0M44lp_zZKheekwRljcot3l5ZBiKIjNKyWjwS1jcAwNh_9Jvkaj2hiVxC0aOo7s8ySZwghY3YDuYSHDlVFYjSBpKN4dIORp2hUklBC6jr3i_4Bd-IXxSbE9ZDoJPzewc3aaoUmNcecMv1VIT4Tjbltcf1s7NXMV_We7KD3veaS6nziO1rKN2xTii7zBEBRnLocntj8mraGiLOvRMQx-lM-ax-18ffVpDQEbIntv4-3br3uFMKIbfOzBMYXqGbP2pigNtRlWjjo5Dtm2f5VkJENo0H4aSYmZJgttkkoziVpS22gnniOGTLAPgxf9rxNX13tHssdMx4sio5FQCchs-74XWU-QvMRrY1tmD4r-RGrgrsJ8JxosYUDGS_lVouDSX77mLT4tdN8O7GlyNP24B7whkuJdyJIKhnU1cPOGHgF_ZIQQuCXGQnnRZcKgCw5ENQjGcn7uDlU6yV2SiUq1WWCmrb9dEO5v8epd_t8PNQuPDo79BuTN6bcKA4fygswOeSipASlWIZUgFHAyDZuZZbY7M7Bi8GTOtVX-2dyhTfixQjvdKyhORMjKdCJZbINz9AQmPeZo8MAAbWV8QZ_m1MsWlYYPXpxgK_cIaKdO9_gGoJ_xmj5YCj2VP8mxURe4eAVvtEBJYtnL9wmkqxVNXkxUwtUEXvtXvoG6YC_jIfb2blRN_Ck0IcxdChg0AQTcDFaX_g76fGcTXaHI0VXBaYYgEN1PaQRZf-y5jQzwZzzABI3E24WX5T-PgLS72CS9JhX_B0Z7EeqeBuwXeU1WksRsKqFSStc7LmASr5SQW5J1Zho0VdyTO1Je-x2P6EfuB4CFfAdYGOtyOktLbzxYca5zPRH10ScpToS1e2pYTUnE8mmV3igripon3Rz0ABQ-5KeJrtq_fjAXR7d-UlhFqRB6pzTx-MKrpeUhdVo573EZyDf3qYNC5em6lUKIMW3DUr9TpVMzup3cNFv622Xhsbblm39QW0JqwCQ0YLVMBvojv0y1aW82kPYfq7ix75vwu9f4PmWILlgtzjua88WPHQAJAWFHBVZTbRW6rcGvvjycRk46iMGQbI5vnNmBb0rVlDsjjrcK_mmZkX96n_WQCQU5u4bOxsn3gA9hasQ8IKxomYk_0K7ASjBumzImEu-DNHvfMWjSM69SrOEH1xyTw95lusSDb6rhTpRHEWA819iJE8pGipYbtOj517mEVxALjgQSWk0TaZ6ycTAFQQO3yNoNoCb4xVPqfgn8MsfbH6I1t2TSG40Q3MLn6ldxxMiKOgMHbb5ZNXE_TEcLcOp0HRxtm2-3wJ-poMKjnKDALzRPOfbHv0T3jc6SVzkffXS2fucnj_rRC3AuYl8hVhRG9SHuSFZnZBJ8QCpXaTeHza_tqNtgwjCMQ0RkQ4oBclZGQrbZ9JW67UFFxrJYPvC5YSjkJGzmXbHzSXF0lvsvcdK3fmhi9Br1TKASeX7ko7Wif5Ayk_bYbhWq-kG8MmO0k1WyuNHx_H8mJD1VJiymX_fbHp5A9CQFahVzI5MDVRkz-mfyXUMEDzOUpo9ZbcjLVCjDZN3CpVtLBqMcoOND2iE94-dljIN6FXXg8hYzrCa0TRiLaQzLk7UHR0BUho6hJhJDaW0k9HzbNEdf3Ll4wzYgopJNe8eud3tHx05TlP-TLhI04P2sh3XWadCeOIz9oIMHHGwzla0-gbWgMvxuwhYrBY24XgCuVM_NpI1tyNCvWIfvTp2QYMduWoON6EK0NPJLfMRWyqE8EDbD1D35fk2NVf0W06A1SFMEtePESkcHa67W9USlO1fTWRXGjNwHq5-uIUNTbUpJ7dIsmHQfKFk_JRpprJZx5b2y7tGvGhnEnPYyzRrVUiXYU_ieydwGbPPbg9wLdJ_l_vPYbVES0CdpnX-dAn9l4BMNmmQTXDZdEWs4_jDNnpF9rkuLjx1IHhh2dfXm_2aak9oVDiyumydE5xnmlrgQNongvQ0cEb25NqGaaaLaFAFESXbs6YorfhsCE4A8bHsKyF6ENBgeIpRjByqPF_snOXdDXZD7Vt2T1Sx58ZrSVSkdGyFK-bSHnxm_2A2LR0OjsyzKnDk0rdSX30la6wQ254R6ackEYuzyZEg5dXLFTM2NcyHf8HqNoBPysX7nKw0oWvedkjYfjzcD_s01sr1_80Tisrnjp3YJfIUoFkr-1vJQOyx0-EGm8V4fg32vGUgmtL3X63Iklv9ieM0IxNagX54NbFV9j0DsqQnhpxaxnPm5N2tbHlNKj4GnPZhjFgIfmR61d3jgsItNrFqwFalJ3GDtQXDv6-GWp76C4eMKR4mx2T0n16GX1uRxLZ493DshH6-BWTzPYKyl4PLfpnxYBWg3yc2AXonxNpoqq1hjKJIuOxJsl3LpB_jopN_fWdc1RLljZ6Ea71GKwV-rTfeVjLVgSnr_RPfdo2Flf6hldaNRTuCMx6j-Lm_SmoiccVqcv4Qf5XstLKQUS31OQOyy-6Vh-HsfVtpBLLoB_IJbpLb3-VBU-fvh_4UTntiR2fTMEkz6I0rmqMJkT33U1kc6DomAWaBG5PnEbMvbCfRFvtbe3hhDx8z9KV6L1qGklsADAiUd8K_zXtR6NymcSJgMw85CNdyLGG7bWAW4pek3eigmJXoSpe4i68CET1zelSc3_CkHkiWY2f3uXQ_sPkrfd_RVut0ppEkFJwUEhCyChYGDG4TBACz3GfsYK0aDJ3IX8kuMCuXhJVImPanx1wBl0pKCHCuAGGoa0E7w1r7p3DKg0WWb6BwkCQN78rxjvOkc6RqPzdTD7PqmYzP1eQfGn86FRh2Ejiy7k5eVEFDW-VwqB_SJ3_h_49qKPMAOyfm2DD3PsIX6-qtO36djZJm8vuA7hMALgrj4XjV0OmisPqG7LTKg5YuCUeSa0zHHkoRTgBGeco_CepEZ36dMuqFjOb5QyNiaG69DZ9aRwhf3rzGTIvD0ADU2Mlyppd72saklSjiNjv3om4ZVzNkfbAaw29bI5OBoLSfG6U1pB9dDVKOvxo6b1yG9SzGoweCYJf6nVyjWh0oilh2bBOqFAYlBtbR_1BrKFZH1RFhjByezhXwIi-3vAToG07y7zRk6qCIP2XOBF64EZav99vMrrRaObbnk2aO7IEFxWaxC_0WAZb4pKhwDlO28ztV-xEQo2PcG5XYRqXhBarv0ubIAfS6u8lAA3m5xgNd_OPtPuuIF9OGaD_N4PcnF62zx1mJdRE_WiFg8_QUps3QbJlG3GvXBoo_pa6pvlS8wgUBkxr6uplLULHhG86fkeZaWEw-AApurDnTdPcbQyhOPVUkPcKNnlUPCD2_PzRjEOCwe9c_gj-bNe1iR5rq7F3829RnZBRzcXrRLsikxeQnhKuK9xHhzc3a-qQJw6EmcXa-Ej_QZtkagPRn8fTtaElzgcO5Exy4H-BS0yRL095etoW_dr-A5ixVl_StVUvP87T9pyQ2O54ior5UInwnrzKx2xKA88CPbE5vNIz0zrN9y0naYVXDgmTf0ThIAY8twAiAuJOf3KkMtQ2u1c7Wqx6V965U696p6cZVgN4fa4BKPOwoNg9n3_ZauTax9im4gEEad7A86xmdeAvSQaCL5_s-J-dseGWOLiwV2xBqTs3Z0PdAZiXePwzXVY2YHEMO3g_u-no3JzORucmJCabDNG2HoLINvpcS7MtJDuXnukcrqH0SH91zlQurli-bthDhw40-IFgQn29ug6zaEPb5S5hQhIBLgwp9-vlF30HeAg0j7b1jjyrRXdjAizfnBxCmZokhsZWLgbYkCVRwzfRloUwP2TfNljb38BKdGEY0_fudwaXb5i0eQJeJeBNaUtVCNHI1yMarQAVoFlnmXlI9m7TX0PN8f5k-d2If540qnbDY0byUrG_5we0fy_wDmMYZ9RsPcZxcaIsskn5d2XU6plleTpK02iW5ULIq2iyzzKYYkif1hgolanO8ic4-JOeftc416TsPehXeVV-kczdZlZZV14IQlWkZQHTji6kJkx5l5JWJheTTY2wb4CmFOg3V__v2inGD-EhOhecDTa_TJcjfUdoq9G5GhijDyuE0S7I9UC7v5qSLYT01DTCsiWpQxc-OWhdAr23iaWztvNB_QZ3ArHuQ6FcEkGG8mzbhdOnni3qArM0JohLJ2YxGb3VuHaqXdpzAJe2Z0OSoovpVTv0AX3s_7_55rHr3HhrEGlEMManM2pr8om_uigerIeXvMGMo3ZvvyJaXuZvgXD3mXsU0LoRO0vyr3lNe7BNniNf389rBEUx8RQpRTtImIOdb986JY5n4VJkyyyJbNfruL38i2huBRndtKb5TAhco9CPHQsb0RwLHAI3u4XZbRkEiMvI3i15_85AVPMUFo7SpH3GDim6J8yBOIaXYk5Bh2NZ7Qu7s-IqGDsPrmZu6mWGYArRD-xigKdLXonJQRyid_C48QdekTQnt3S6dwIJUMrHycYlE5io2O2b50TDy3fjvceM5VMWDsewTEYn6qDhQgRnM4hOYrwQP_zkZRUSEDMsgdbo1VBgaN3v_EiNFlQx2bXIbjh9cMjewK6Y8NXXQbd1Kk4ylTHecGya-9XrMj1qgTCqwXFkNkoh1t9-2Gzl7A9oqSXH2r_hX1RUqgef0nwfOtdG7-X8cGpY1R6pFiFciz0c1XzwJYRRqof1w8_lM0-baVJm6F7Xw-qPBCP5j0LtZveLP2jSdewWFD9FuGTO9lPqcfxA50R6oVc-Ny-dkAZNTPzrFJPqbppz5VwaPFiFmnkikdIhx5d_eMUemwyFamr-YG7ya0zYEe_FM2aETqzgB7DVpZXJ71ovplms6OjPzHIldjDGZd9OV8ngfz3BlpEBjzY_gc38iMWV55v5zuyCiRIQ5oIxHLqNX-rZzYG0kedkYLwFMQDBPxIq2pCmVnX_K89JR-sBLXYTbJrC_qClCivk5B5fPTwgHlHu-NPgEEI7x8pafXqfR5I9PQfjA5TTOpyNnei9LNoZPSZnbpmxzXdBs4ac3SJ16dNlUZejcV96eIxzFxfVeWmZdyYSmo-gJAN9AprI9cnkpLwkNcbgUQ5jyaoZNdqYLvakStotX71OzKckxKelMBI-Ei6d8sAZQ3r6FtWfUeLGHXQFE2BFMH_shlBckHnxpl3wuX5qja-xhgC_VVyqKDJUC6vF-aTGSeIr7CehXBJazTmRGshulHj_OZHSvrcs7GEXGxWbmPBLsXSK9_XqyUmvGX-2H66y3DCIyVez9MfNhfVtEz1zEy9OQx4MApG2b47JuLceKkt35tjeglWypemf2Ld1urdoaVwTPgXgIA_n3t0izhsZx9XtmzrNkG7Sej93OG0uzzeHXTMV-9abs-PXalCtrX_y4X2vxkuo4kk6YjKlPIwdObaXz72M7oxhxkNiyF4Xht45b7ZI7ZwNA3GdOgduLUPQitVcVd8oKLYwpNJt8AXSmUAtTRNkYOnpqBmab_U6VJ7326KxtpgYKmwoRezGJQTaFNVbtvKH4O1p_bkWJtUZ3SV7koRtcdYEbvJ76nYlfwjku9dmxfoqgIEe4tIhdIwDVCup_EJM6mwXz4671pwt-4-GeKKO92jsblL09DqGJWwQwkNGYCXJim-9KTG2484BHNAWKWNWsOKW85Z0VejE-a08DnXL5PBNwzszsFIrgn6QsBxSp-TyGZJSRpk01oZCTTtLbwLxfI2-3KGPXSwwsecI1SEUEcLGSuGdewpIiEsyPyaHTPX_RdADQHzsXJkP1TThq45nOA_SJE-zriaYVnexf9-bMOhhiFPMlQhIySVa8EMIxzTx8IkQJqp1Xlyjt6HTqBKdT03DM9SGTIbZSEzXDzEz_eh1RPvcbmDwpp7t_R-2RvGEYszoNljkRb4rUlrrKruwA0NkNfAge9b6ROZwvHyZwCKDOAJ9lI4vYPH7A11-oLBcee5o-u_NBY7XEP5gyaZNydIrnNQ8TXANvtvC6zeEblWdbb1a3Wq1S6wWI2PlI6fxFX35BszMU8PsETMoXv4M3Oo7F2KPXE5TC1-yGtLUtvU5oGI6hIqO1gcYoBksrEh7OQA56AufLBwbC-NwMmN8ujR9RyzfnFxR1Q_Udu9Wgm2jqvyPFOIWX0pG8eS7KoO6BDgyXRafsoHZ3cI_hFeKrnd-HHeib7iVtBmFN0GZS7zIpgkSvoQ9JAh03hFiLXy7uJzjCEPFSU0xMgQXYOXvh0keEkB5N3Dlenb3POP7jvLjLW1VexjPU1cAttN1Q3ouHLyuROz2jdXePkowgxY_8BMSt6Ng24WktFIf9LnnlEE-vFcv1bwz4sgY2IYfcO5SSD11gy0EPL1AMZPxAhjKYp4v7FI4pQJSK54cNOADHTfHup431kZeOaoHzcv0Ke2-9_9g1-m8z7AaaFhSk-N5z8kxI2sxlCkKS3xxv0UDGTiTwlVPlAkocqFmRdmx58bqhKOahFeQrpfQFJlzcO7UsEvEy79QS6NVWt84g9pTGHEaeKcaOeZAKiJd9O3sJv-ozq7_dmGg2pZgID7SZPR8ikPz0z3jwQ1nquv3m1qaeQs2xcXJZIPkSIvlyfQEl5SQoFi-g5Q5CmGMPHvJueRGi4vj6UYnarKb8TacT8GKn-8LpSg2tzykBKpD69wNQQTx_vpVoxFfBRsBvpvCjkhUw3zWlwFiS2IuDogYu5ZpAhII1o2Zwnq3nMnCec5Tfh8IFejjRR_MAKTjk1IxymehEzJVAHm0Kzx8XvPhT23HmJbx76sf2zryczI-v7J1cG8lwr2p6MjGUyxmczAGUfkEs_p5BXtbEau4t9LahS3Ea7JJOxBYE88OziHHuO09HLq9Pv3GSTSz3ZIzrtq9lycNCONAq6KT3fsS7wVqwTe4RKVzAPr-_3BtnJ7zFCHVDp-64XjNPxR7G7i7K3kpXJSVxZWC0nKoX5LSyrG1Rngu912LqN3rmoS-mxIcLawGIPo784Fx-EDcOIqVunkCMJ6IeSl3Yde4nidRB3qkgm-O4ftxO0958FiCDp1wkJwVLTjkJBkOQ4JUwuFkJ4R3foI87SLz5AOEOkJqr8ypbr2akAi0wfo930_DwouV6x3iTVAfhBok9AUi2plaCnK1SpIxpe_42_Qd9LiNTwMozq9qv9n6vRSj7JH8hx5f0MeOcRnXJBU8kVWSx1jqz5OuTMgA3wosf86PWb-GwJY8qPnTMhdL8jkm32Qp4BQnZpvKfajtuMTulH6VElH2vmaiDnkSn9TZLFyeuu_H4DXa3hBf03IpxdJPnw-Ddt78TNR3t4eyz1ayYMD5qmvqu52-4Q_hpcXNDAD5esOVWZOO0DNvKwEbKh5s6EdkbfcmhTRW4TR5ykzLvTuZrqP2cez2xo-MyhnJahMuCj4FxWAwkHxJo2y6l3p62L_Z4Px_C9RapTw754Kwnec3Mk_xNTeGTnTNvj0f9T-gRSf28K3N0oz7d8Gty4kJeI.KY-nIFAGbuWNxVmT8XVeBXd0UKrOMBqPJhGCKBU19pQ', + options: { + version: '0.0.1', + compress: true + }, + json: { + credentials: [ + { + recovery_id: 'jwt', + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialPayloadEncoded))).toString(), + }, + { + recovery_id: "anoncred", + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialAnoncreds.toStorable().credentialData))).toString(), + } + ], + dids: [ + { did: hostDID.toString(), alias: undefined }, + { did: targetDID.toString(), alias: undefined } + ], + did_pairs: [ + { + alias: pairAlias, + holder: hostDID.toString(), + recipient: targetDID.toString() + } + ], + keys: [ + { + recovery_id: secpPrivateKey.recoveryId, + key: 'eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJrVXFTZTM2SXFFaElueXhzd2tCR21hYjlLRFBXRURFdFlEU0RQTGhSLUlnIiwieSI6IkRKOWxoTDZwVW1FQlJYM3RubzdUQUVBMmJuR0hKRVpjQUd6YkNRZ0NXSnciLCJkIjoiTExXOHZXdmxpTFRIc1c1VVlveDVWR3RwczRzT3JyRV9yWTBIZHFIQXdOMCJ9', + index: undefined, + did: hostDID.toString() + } + ], + mediators: [ + { + holder_did: mediator.hostDID.toString(), + mediator_did: mediator.mediatorDID.toString(), + routing_did: mediator.routingDID.toString(), + } + ], + messages: [ + "eyJpZCI6IjMwZmIyNTU1LWM5YjgtNDExZC1hOTY2LTg0ZmMwOGJiMWVmZCIsImJvZHkiOnsiY29udGVudCI6IlRlc3QgTWVzc2FnZSJ9LCJwaXVyaSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvYmFzaWNtZXNzYWdlLzIuMC9tZXNzYWdlIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNieXVFOTVXQUpZaFFhcEhSVnVSbTZLTEdiYjc5ZjVVWGlhWVRBWXBVYlpnZC5WejZNa2s0U1l6TER0WkpudVJOMlZKNGtuWmN1SlU0bzVDYjlVVURodkF0YVVaQ3g3LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1ScFpEcHdaV1Z5T2pJdVJYbzJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUzVXZWpaTmEyaG9NV1UxUTBWWldYRTJTa0pWWTFSYU5rTndNbkpoYmtOWFVuSjJOMWxoZUROTVpUUk9OVGxTTm1Sa0xsTmxlVW93U1dwdmFWcEhNR2xNUTBwNlNXcHdOMGx1Vm5saFUwazJTVzFvTUdSSVFucFBhVGgyWXpKc01FeFlRbmxoV0U1MFRGY3hiRnBIYkdoa1J6bDVURzFHTUZsWGVHaGpTRXB3WXpJd2RXRlhPR2xNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREU1TGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwd04wbHVWbmxoVTBrMlNXNWtlbU42YjNaTU0wNXdaRU14ZDJOdGJIcGlVekYwV2xkU2NGbFlVblpqYVRWb1pFZEdjMWxZUW5saFdFNTBURzFzZGt3elpIcEphWGRwV1ZOSk5sZDVTbXRoVjFKcVlqSXhkRXd6V1hsSmJERTVabEVpTENKeUlqcGJYU3dpWVNJNlcxMTlmUSIsInRvIjoiZGlkOnBlZXI6Mi5FejZMU21Zalk1Y25ISkVEM1J6YkJrb1F1RzVLd3Q5dEM3WHIyNVlCeFRmQ2FoTm9MLlZ6Nk1rdDVCNVR3d2NVaHFTVFZNWFUzV3B6cHhSTXF6WEd5VTkxeUNpZHZ2b1BvTVYuU2V5SnlJanBiWFN3aWN5STZJbVJwWkRwd1pXVnlPakl1UlhvMlRGTm5hSGRUUlRRek4zZHVSRVV4Y0hReldEWm9Wa1JWVVhwVGFuTkllbWx1Y0ZneldFWjJUV3BTUVcwM2VTNVdlalpOYTJob01XVTFRMFZaV1hFMlNrSlZZMVJhTmtOd01uSmhia05YVW5KMk4xbGhlRE5NWlRST05UbFNObVJrTGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwdmFXRklVakJqU0UwMlRIazVlbUZZVVhSalNFcHdZekl3ZEdKWFZtdGhWMFl3WWpOSmRWbFlVbWhpUjBaM1kyMXNlbUpUTlhCaWVVbHpTVzVKYVU5c2RHUk1RMHBvU1dwd1lrbHRVbkJhUjA1MllsY3dkbVJxU1dsWVdEQWlMQ0poSWpwYlhTd2lkQ0k2SW1SdEluMCIsImNyZWF0ZWRUaW1lIjoxNzA5NjMzOTczNDM4LCJleHBpcmVzVGltZSI6MTcwOTYzNDA1OTgzOCwiYXR0YWNobWVudHMiOltdLCJhY2siOltdLCJkaXJlY3Rpb24iOjEsImV4dHJhSGVhZGVycyI6e319" + ], + link_secret: linkSecret.secret, + version: '0.0.1' } - ], - keys: [ - { - recovery_id: secpPrivateKey.recoveryId, - key: 'eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJrVXFTZTM2SXFFaElueXhzd2tCR21hYjlLRFBXRURFdFlEU0RQTGhSLUlnIiwieSI6IkRKOWxoTDZwVW1FQlJYM3RubzdUQUVBMmJuR0hKRVpjQUd6YkNRZ0NXSnciLCJkIjoiTExXOHZXdmxpTFRIc1c1VVlveDVWR3RwczRzT3JyRV9yWTBIZHFIQXdOMCJ9', - index: undefined, - did: hostDID.toString() + }, + { + title: 'Version 0.0.2 Provided PK', + jwe: 'eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYXB2IjoiVk5BTmhuZFl6dmdXdkVhRjlZNHllNVNYRXJCLXZSZkRTRjhfX0o2ZlVUTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiNC1mOEpqcXVwRkg1b0lmR25GdEYweUZqZ3hNVkhDcnlOX1B4OVk0bnBRYyJ9fQ.VLU1YuY-5UsmQBXxRTArvR6h3f3qwnPPcXxwSlqunkWf2XC7BOSxgUGGWpV7WQlBOIllngu4Lo-MohyPH9k_v1iXTMHWkpr6.tHcGFzikES-utb5jtXAamw.VOe8fZo-tQI4K2ePOXQdaHsYnIyIJ8hj_sEzOEdpml32zZe6wyZT0wL2WC9aBT9tfObQQvyheECqTehiwzXsewXfjhYVblbMz3s3Ux7rS3tKVXJ_yzyHfNUWJaRFaH7ThcASLG8plBCu3VVlYR-TG4ztouucUp5sqZRN0wqyHqfgZsNumOQXeNZjxc_u3NxZjCTyzU-SCdHliRIuP4TRjQYR41JIzUDPeAEqsVk2-9A2vrZNdWGMjReg8jFnNlyMWp3cLWRA7WRhO6ajCjW9n1C_RolsF0U2HS9YrqZ_KD7BVXRD9VnOHPkwYWv2dpLuMJW6WR1XBorohkBdmV_KogLPZmD8lumGF3dgRw6m1EjXvdEH-lvoAFvlpJtUKlGLyeaAId7tROsosWgwGLmzZEyKrO1DQkfpVsqWuOImMBeyFhoyJyRtX3-KsBEl4Q_rwrEF-b4iP6dhlSXDW6XJ30Yh8ks095wJ17RZltwn0QyjV6ivxa100VlTyi49kPPcpWRWwJ-Dc9rQiB982LYa3AEY6tB5l_Po0QE17uS90N8m7vUHkpGSF8hLkhAWsnnhGf8Fj7mgseTwHX3yNSkqC0Psu10Dc7DML5fY4MnpAuR6ydeOTId2hPj8EMn51f04V2Ldo8w_79p5-4n6LGR96NJtmeR8aG3SZ7PB8LuU81jocAjMif6UtWGhF8AD862PXzyQmT8-UIJPTDVLUZl_B1VKmIdjjSpiNS8FG1wr6taeH3rCRfLT3bgsMl4PlW02t2A5r928BUrEYEu0_QH8-UPyaQ9NEt7qKllPLlIM5ll5eAwjSAQkauGRGqkVOvY8LjQkAFLSZiOx9nRy7-eHP6X3GTSlWqgodTJwFtbJIf43aN3wgHnctObIcGTFA35RTBuAR2Lcy5qcLGBwsVmkVeF8pvKsBu0nG3ovGQtSnXC7Ix4FYcZAkWJQg8Y1ODkHiHD8QfMJ-BgeTA3DjfOce0bFHXjQFkQDxvXTI6SCTzVGXSJecrguOEylCgl2akm5Fi7JM8th4pPbTuehb8dswmMNRZbcgTm8AWA-CBbvq4fM2W-656AHejT4uU5_wu6Y0XSGpCTGrvATF-kVSorLafXfJrONO7YI6k8QnZ2xUZfsXH_IMbJ3Hzi0U4mZrbFM1lnxSIbSxe8M61psLooZ0xihCqhBF-1kJDwcjtjcvUshkU0sIyU8tVJe0GurH5OznQq6KuxJgRyW3H13SMJgNA6a2ccSfwEU3jRbXj-W8WH3xovddbbUhAmf9ONne64PCsyjUigqy4wlckqED2Y-lnmCXBC8RSdbK2OfyPaLo_OWpbz6D5JffmKLXGMkbjofLGYqx6Zhv83jNH-2DTEWrSIqAIxhrzRpLJeiamA7B4EMHSPHqxrtCf5NMKonVxRC2GzLt1a09bNMCyhgHGIpNmsux18m6qvXS6Wee1EEyBI4Rm-C_wNxZHjg1OV-6N7UWM4paWPNqhaUoDWcHh7SZcShU-PXeA8vS1kKRpgjNTgf1ZYZHIB62ruMIRfMBclJAPmosWBOCfA1jcVuxmgr7x1RdyuzJQDYISUVOThKMSzyXnjFae8kOsV6v3x3NxyQAST-hOA3dcf5SEGz55FcF4Fepm365QTQf-Z8rXqYkrbqS_44DFtNnjrQGewJ0p2jwht3RZKHySmrl-k1r8xavHiseK754cvF0KLJBWWIiz-oyVASTUsbzkcNRVbUCbXCOhdr-8KXSKw0HTXWgRPmw7SPPgwXBlbGFmPDS2cfT7x4lhBVqUh4Hy_u0heb9zKvFB2FOy75gkJO6TRP4oQfWT1CaAoIols4OymcizVvi__KTBQQeSDgPJakDBdU0_4QkfcG6ZQ3SyxqKBNC5P5OaVG7Nh7ujR7pcsh5RJ6Co7wi4YcamdzhcKoGtQ8dlfxuqYqQnXFn1Zii48v53p82Gm68jSyT2a6pS9LfaTTTugQEg52dkxkzhHNEC44WigyxW0rWMcS7LRzcPCYwiiqY-bBVeNYWr_3WGr-OC1gbXjdaClG3jpySeFlm1EIpYWRTCbesftF1lJTVAG4g5FIr22OyqCuncffoCaVlIv1-kDJBY35gmjmu3ZPasV5Vqz1p6MFQ9vPugV7dgxU5rIA95KU9Q0tmsQmdMWEm12bZJqja6ySlxjGhCqrZ2tUeeRYVUtGWbi2zrC7g5HqNS_h8i23kGjKHTpmWoY9BXpBsjvtk4r47NZcIB2afWugGLxi7IfCDsiw3C5qjhDqmU9xIV7Ib534luoaePZGNGev7Bz_rB-1LZc_OR_0-Ui5imhN7IDqVMy3ncSAsjQyRv1tjOvbLZlKOHDcVmJiVLRKqGyh0hCIUM7idPX9bMS7cp_lDoSrjdCjtpilYP6bg-fgtF5GNgYd1orqDYHD3ZTWbgDSDl35MrI3DtaBhQtsm2O3OsrSejBvnWCJoQzMs9AammpFGXnY3q15iFAgPGr7N4hmAg5wLPBGzps2Fd10InETPVvj2seGK5Ab3k_cNqWFM7UEEWFbv89dii6hS_akjs1Nms5YFvThUTn7oIyrlWw8aABUfiCf5Pr4nqvAcNus0rm_iWUTSb7v3V3jqG--6vtXZNuQXByeTHaBG5i7eaLl0g42-NdBBOX0v8MLnf84eu2BN9c2SAdUhWjBmQVPLA97-OTF8DM65nx-3WfLD7VHjbWfPO5DJqqUpFWEM3Mf8_IIdfnzysIX_eR7daZaNpzx7qeBiEJcDrWcSvoHDRYEkuDZLPnc0VWi2jqE5SX_ZRUTYfUyxZN8Hf3L_JP42SXsrOdhogTd1SC2RnBytme7qHDZau-BYoaAmlPQ4BwPekAnwSLzIVUhQJ05fBiUAeP9RLc52RVjHDkwdT40x60Y6tiz_dHGLeOcpDtZCN21vjAHAZUWFK0lnuM0FjEzQM9Vk6W1FoA2E16k9e_Iqd6nz9C-HxDpkS44vN3m27Jddd2pPVIBOZy-pOD6WmbkqsBnvnxKUXsRcPjlROFntnWvhfMY7NePff7RHNQGzcaKraGcb_nTkji2vcYRZjstFTJCSrk3_G5xZfFCusUI6QyVrDiXcDS9vBJVaL9cpS6tt7Ya0_K7GfW9O_Ij3xFXViiXKblYJBCp1yoG_zNs_pKNwrLZuCh3W-MioDw3RvmpaB5taxOJVZLqcsOyGrzXZ7Q9ht7UbPu-L15hIGNyR0wbkOUoQSZ0YKcSQ61N9elG3OcZAJWgfxB1aTmZ0HkFipm-xYaGf1pbGYYFVutqxzlCSCB42glSI98KsgNjhlb5fUQcArI3ZBM-qISeCSIzLIG6-qi5xmOeBvb4WBojnkRBrRlUcVDw3oS3j96c8m63G0B4BNEa1J7v4YvOEsleMIJZlW34wKZSH-Rc6otTD9_0lJgppdj8z9f-fp8gbo0MKfvL0-txIiZdkw0BFrpNqq-4gqrHuYHb7-O5Z1qWeqGUXgS4_xXdpQVJLByg9pUzVbYzrFo8P1iYE_EnvP_xSiOzq9NabeF9MXxKxww7YxttNG3cW9uwnTvQo8YZOkuKu4XyefcUIRNt6pyC4Wb6zxLeExcXYNNCy6U0XkNj5zmzpdnslFrWF008Qc1K-5I-ziEfnQ84f9WE7MpNBqvv8iaFqOX4cwqQ4GxxkkeYzZzUoM1Ver_WQSXsaq4sGQh9jCefiyqGHWyzOzug-e9P7SM2uY5UvLE0-8qe9z60zSjXHbjH1TOJBdQNL7SPGQPcluPMIKF7zVLBdphmRgm9aX9Lbacz0ufyGmbmautxbI3tKFiOglXJTeKgzvqXFYJGKlILxwSvFiVzt2fANC3O414VWbQxgbBeooxPK4tZAJ3SXdoGxdS8UxtHSSHaS_WBDnS5-PNHUZ7pvoeKhblhAnfJg7qMXEjYWBNIwSTVA_ZUBYMaB_3XJyso-rZkUxab-177kg3GEkqGtkITwPzURQnSIm9NYkRvpR2hmgzxTi8ioYWilbcKkvRdjDWdQY6I2stwihvtN30gAUaVYkR6Jr3H4SsuWWJUt-Zk1CgWUvl7tDf84oFdzGjk2zGs9E3BPRO958cnMNmu3Yb4nmfEFYtoyjYqKVZZaUv_HC-igglD_BSURKlkv2faGPSHxxBTOfMyFZFZSOiuuNOCvujDslcZpec_8oYmVxOHX1wCz2joq_Yl0XRFkbZjsgDWF9uxzl-HHreg2mHPhcMXZkFkV6Va1_auB-6TjftxwUYnm1pXIiBpWal1H4wQG8spW3AcI0GQXiVlkjzLt7_KWewnhMYpHbjpIZve2M1BUk-HJaK1DFRKq6-cbua9lyBxB9Fg817XNSRn_HQw5Rf9Paj9Bw1DnrYGa1SzihYXHWZ0Bi_S82g4bxVGiSGipMRetvzResXHFkmJB3SPXtLu6yGeuyKa3IX_4_4avkPvH0lKPR1pJPHiA8z5Mq--w_qHaXk1gvt5H9fe15MH8V-2Oh_e9DPi25CVl3uwBgvQC3iME3KMQfCrC04buIOwHSmwj5wufMoJ60vDbsRqM1_0JDDvMUIHzj1fY_l1bU1ySN6Noj03QkhPI67H9LyjBz8Ic0OsDSL7s9cTqphmsnEdSEUoVqKp3faba5WzLsuCGgkUEFjficXgLjZeZI9CpkrUMxGr7hNDsM4318MM2gcUndNNaLurq9T7A6w15TwNnH4hAVosf3Egz5Se3RwKe4mNAr1bRWlGlTDhNNBF7QK774zsM0DlqnfQRBek4wv0jo5OgcZnu0bLzIwSXQ5JPGUaI1RQKLkxIv7_sSWfIC56E5LqL2ruvPA397ZBLPe6wU8uX3p01Uc7ymeq4HVmr6FhQHXKQKafl-BuBdGWoQR5q03tkZcHutI7diI4R5hPIBxAdoNIAHb2v6pP8CLKZoc5NYPwejiE-HpJ4Zm2p0G90mQ_Xw3-biD76E7aiJj7oNZqy7cSBQIo7aF8trj-tDVKcZ-2zAhC_DkEpgzpIQcAOWZtIh1QfVeua49PNR_GjRvvletcu2XHkBSaXcF2Cnmsf07Dx8kq4gXOTlIz0_oPj6DqwxCBlkTaYFG2GDL3IuvLzcGLdJksHwNHTLGGdcs4Q5UOnTvficiYABDRUev4-2x0HpLHrpk-WSfJSpcRkM_XUYPiIF7qdBpPFPJ7BVs1bdIiEgt3zCg4gqQIH1kToTuchSVQWVkWppCD_hZbheKrBOhaz02tPhyYVjICklN7lcdmSmR6-VVNFHF5ZAOXGslVZ3yK2aN6nwj7AB0lplOoqnc7Ky6EZ6Koi5BBX6u0xsL6Ajml0YMUH3DOf0BQmEiiBUtoG6hS0UQm7IeIOrlywFjvVXYWV5Z8_-GxdqsTIQVzF_U8SEv9G1szMA4hEQLMCS5-Kzx3hseC0gISen6APMPWXfrQqwAl_S-rcv9vmKbMKBYDjuFAmv_diYSCnLRJcM5eQlSkRByXu7D3lMxPYppHHMBJgUXUJlByAu9maM83fAJSPEvNQdkusx4Tu7umoqdlHQIAhdZMsXeDHA3cIxud7nzkr2nRfZNs1SV7gPsd7Rwnt1BP07CwGjmzoXI3j15uuD1yE6fe1x3kv93yhX0TQr_7FpCtMo9NcWcXkQW6fTQzih_vvr26QeBHLMCTtIXVXAjtbNpBW641Xb8gRS1HdUmUfhOv__rU2CI7Wmo6k0s4bf6PIMajgQKENlgDp73QBYmnovb3lOduNdUKFv2YDRs6OPw15nYkp2P9pKFw6_neb9UTXBEW3tbwGPzftOUDe0HpLI0UyR_CEo8wwt1G5hI_smDvjrHEY8l8wolWanB4kfqnGAaSu8LnJ2tqnrtu2LohbTKReem1V7tgFqoGD80t5i8RC1wPXrlas4kIABBnEdBfDmVXFwPcBhZYK7QfXqDmi72ksHczTv1P2896FbQvVbm_CCUmVPQO_7l-2YkSzG0SApQ9zfHYDMut-xOvF8KcCsMs42SEqFS849RhMjx0ivEnStfe-BORzQA0fouWm8bx9D2KJFLwUxkOtV4TuIdjvYz7ge_hJ_UKin-KuTLZEBO1d4F4WJZLRE-fCaVt_tNkdZRi8jzEbeR0l-6wfLaxMWNXl98f3Rm6w6rJ94SWQq4X-rPfZ4Upza8DF5S_kHxCCO2U4rrmPw1508VDAOGqSNfw8jh-jKt6eUTwRamgprGBCmqSN_QLica5Ooig7SyivMJK-aRi1hiElv-cf9IPxuh8UNYoLwW7-ySZ4uewisFWuI-gso4fuKemXT6yW9R6IYuylijKN3mISfIpdxiVa_2n_iHIihG-bDPTxFP8V14XWEYfWlzWZtLyjvRNjMzaeadE3obUMkrO7-Uo4YLgy64J9delbzaltKcyAJzM1rU5HtP1cIa6jp08QaLS-OZbJedg0IBsMjG0OVA9JfSY7GDEN3Eb-uwBgLtccvvZbTqvI0MIN5eYiHezzLf5NlJdTPnc_QJRPslhsQUBUy0HA891LT96b3FaJ-pI1JHKmkiQ3LjCy3ZRGRPaoQx8KTfy-YHXW2i-F5pt1utQXu4CCNe-qbEAUDrtiptwnFLiVCVP_R6M1ef1jFFB4PK36BJ5ffUBo8_702X0SvJMSSBz_CTueHemN9NeKfreO31Y2UHyKFWTnKu_IqWQEeNei_ezakieyWCu1C-fBoLqMO8AAVpVl217Ul3JhMmHs5XYZsoRyMtC9puxqwRHv1jabjGc9-oBbUPvG0OljUAryaoqV7aMcmUCcnNkovstLFy2d73XZGY6ouIPwtLSJVWJYP554h8qQ4XPLvQK6B0ELmjEniBw23ThfOLBF4AoELO2tsaLoisAbueRfUTsMe7G_Qmi3_RaWizz_1xQ-vF1nVW2AlWm4JrEUTekTMPdjGm3UkpCnBMj9o-mz4fYJYptfmVJn8pfJEgbACTMiU9ZnUYR6yGKKnf8HbUheJrWCg8CtDQhEtjR-OXE7Akq8vEArygkQdCdWE8sVZoRBFFWT_EJto3nyqogrX8y3ntMHq3zfVDYqtPHbkra5fM371ljZgJ7DJ0hXnI5tYtbL7DS7LHYOUYqTbSV4tGG7eT65HqVeBQbpz5OfAd6ooAh6M3JusD2V_2egYRBTbGQPYC7XngUYllxyVu0Qb4ICBKuujRoOoh_ucEVPIOU_ROAB_xJPAUKtvvHBSPZClY6pDhySooq_ziBRptubfPQ_GfoIUaU61KEIGfmQinSXNpyDEDcAoVoTRikS3ECbupwR3pit4YdKQUHd5HXiJM_REnA7kdxaS6hOPKbE7CmCvaVmL9n70HPth8qzRXyu4o-d_J-o6dAjs8GQ9CWpFSNXRnatAqMx123Deha1bVldT4zt1z8XJ0YdKB2oCf9jLvzXc75zO6i8fhnuAkFLiNdpuqGvPt27THVD6cq3D7DgtSkjCYYaD4HXZIldo9UWVJfnM9AwDoYdxZSqd1TP-8ACuwsoL_l9BZAnoziufJn-gMfHm1XbhiznR4uAtu6pDuntklqOh5paWfOQctZS_hHQiuNA4N2cxHBvmKpmetvq2vamagQVKtF4srW1iFdqV9WyDE_QoRm-dV7Z84aDH1oyo6227kCiBgTAp4ejnibbsrmEBRs53En1OLBOK81YThxqgndqRJcZL_J0luA9gGuSPXo76E3hB4DYJkCt7MxEGOjh5qOKF0v2zYIZY_7yf7QcibOum217drE-xuKpFgj_MSrFsEDccPALQlYjD7-vfv2HU2gN6j3LLcst0w18DvarX_fsYDtRsgXZ33TQinghszRXKpYFUnDj390bgvzxzvMa48ibLbIjhRCBzAIreKZ3XKoLL6SYG7Hr1AJWtcaDuafsyLjCr1jPgv0q64huq1lJmoIxguGJ5k0uPGBKjnhNAaA4b9uIfAaLSLX5RCrXlZUVl8CXq1Xy1eA3-peE5OeWV1n3oNs4HXYJqYGMiCbW4aDyseB7w9jQJ69V-GrNMcgZWozFDten8DC1h1FoPOvNE4hBvFjmSpqaKKft3w2019OBT7Y7XSzJFQ3kZmAUVLzxWH71Xfe-vFluXCURpQIGECddfR2KfH8yHIX9gLFWLgWnX5hEx0krdAEzUOhA8xzP_p2d7TRT079tWefwJRYruLYg_wzzi3VHeJLUzeqI_WvM4do-GwLQkupoioN3ABH2QCDSJOBQdhTfdggP_qMlSirD4GiRTq7TpdYyjci7Q20h4CUHiVtNmZI5tDbhlL64r0KKpyVGdPyiAnnFzEKmOBtXW_aX35oOFrWuQ3kWlIU-CARcna-WJk9EZiZGFkeRY6agc5pbuhy_cYfH0Ir9Y1u95cYtlaQjwxBWcVC_ox5LZMIZ4VgodX_Oz6Igfo8Hhh1MJv3pBwsq1Nd6xuTFrnFVKoF7YOyA0saWku_DDg1B0iEiH71WSmOtvSZrRX2W5gLvWpWqQGMc7vQcN64VZMoIoTj-HM3E4Pp18UQllS1IXm31e06A4DGMT9KEdFnLA_y5185t2RjwSsKqKAULxjJmYXBGcZEMpeBfwbZHbY5wxU16pl0LKsZPt8FK_yrB_BSOJU5CWE59GU2bSExInOIpHDTT-_DTGPsNc_az5u0yqREyxA15OC3JryFIJs-RCfLqLDvQbp9FgsTghNe9T5dfJrIWLNpCUZ4BPPkxKrdAyKh9PcHEw69Cbjw2kocjGlXbCLJSn-5uqkR4Q8mZd67BN6TKjdvF0dzJ-R0YP1NGeQjmT4ifVqSGB6K5vh4-X6nTLRi7E8m0ihsIiB2FA6EXLemO5YdXEdo7kxw5J5H0c59kMtbyZkDpkdtwZD99xSQb2vQPGP2lPhSUsIku4luJBqFmS3aNqskK47hQo0dKH6AmhCPbxhgzPnjUW_q7lMq3j89GVoBvYGKBIkVWL80Fh2mrF_Hw_4EzXj19rBTns1EEF27gy5vMqLTOnp5B7enZCQPcatGXB3F9bRK8OtRqfc0KnC7Bfe0RvO2uWOHdCKXz8Xi75w9qtgX-izci2rCIbgA_wxFOMXOGmTU0No6cpiSCESQjg0zqPBl833q6OhkUhS1hDPw5YZTlP6TqUs29mYTNHwjfmIbyq4BnQQMvO8IjDvvRi0whq3BREFYSmaz3XmMZy4FX653ZNUFRwo9-88kJAm0or2XueRcnNZAahFEGfhwaTCjqE23w9eikIBu9VwLCc76L_Bzs9No0QIrVMrasa4nQmv4DM1doryDGlfT_CGXhBJa9pYi-MdDj4neKSpRdcr_qMYvL91-fr6xgmMsHG6q_dYwiyAbL2SJESyFeiCvyhm-NLy7KhOXVlfm7W3BvAOyOLanIAddCzurD7V8ud79Bttckb5eWUecUvxtVmIhgZtqwKoyG0eVNQ1Zsf0_zcowhetq5qPrEnNS6I62N5rXaI0ppozSKlDlN7bDFxEvzyT7COQXkgKPa0CMg3WzwVGGPcYCVUTPP-pIQZEfDrxjp9SOMZSMUWS8X4hqm2itMHidjEKtF3fbnqZLwr16f1lctUL4cDS9IdO5t3YuBalmFmhVVdM73s0roo1XEX-QQkWviV_C0kIRLX4yhSgS8JupIUrjmCYE8FmaadIIODUICogTPBWxlrcjUXokUjPNub6NOKzy94k6g94ldI08LFCyzjwGoyxc49sjd_NyRIMWLmX3Nw3AMEQIfnleiAwIEAfd85VIuQBmE43mLocPmM-ObkxcJH4lIy7Xato8cbhj6JkmfuzWP9y0Ie71o2OxC4KD4qjxIW4-S74S8IcJLAFntaqmgQczA034mZJf-Gd5ych2rhaxG6m9YAkQJXTvi5Xkkpmw9Zpw7qeNmjpKiIO29-fvk7gFpfcZxzPFChPM7j5S2brTgD2HItu0kPOFIwtBgDALYp1sdxfvbUGZYA7673ap-K2c9YSgkqbizjoIouGEcxG44oK_tb-2D6f7x9qaJ8V_UrzfxW1QnRookRbPzUktx4l-O-MhCWIujw55WNHVGPEA3TnutY4g-pKOafttAss4sG-mmHNKry-p1hOLdArnuGf25bX50sU0csR-FYqnkz1tTIgNGv6yE-JrsjOa0HC6kQWdNbuoKsG7wgKW3jrsDLNqh94y1PL4Txw8qEmUMvG8TKkvdMHKtpdGNeZylxj5wrUqY0OxL6oWU8NMtbcdBdnL_WB8Ewu-ERjAQwLJh0VqEhXGubiwymhlRbDCf9rP-Snhs9Ac40du9BpCsQuzfciM4iyi_RxjqGDI6VNP-_UWcQ_iVFMAz5mbGMK8McdRJuqexWZaCUUjxbFlTH6H6xNbo6x88PKfIp6ZQei4_4z1cSls0urlusR7vyGbXZRZAOcskjUs61Zk5wABk74_zgZ2iBWmoN0n6X_BcQup1GTq7YUgxGx74mqqLcdxn8oRf8HvPIPG5ftyovtb5_sRP5DPpoM0Ny4BtbjnlMk25KaU12oil8kjBF9Ttjz_VnKm5wMqGhqG25bu1e8DU2rG_HOQgwH5k5n61xuksaL-i7acoQeZGeQ9NrCpgJmKrU2afD0Z1Q-vmiFb9BTX8HKE-Gf_RPIFqfTckre0CqkJVWmNb_UZLr5ky96jK_IaxiAF8YGPu4jYALimY0qF5ju-Ycv7Y_yMkNEqGVgmVBMpwlBo1VM4AkOFELzvQxvs81t0yGS-5y4yHPW3dyrEk15m8RnbOt7IwcNrRAkLhJQOMmdd1nO-kkQN73oRnj_2TZ6YQ8WiI49OQ1gIEuR5LbBhDFtJZ1os04eLhhRKRVJSsLpZdzhfPrMzdBfw0tKTkHWTIE1boWBK8o1K5G1m_XzFvI86hH0Qm1AnuYUIwCvrYSr8sA9DFHgkiH8d56e_gFggBPIqdvdTezY4huJFBYDC324ZRKK0htamzaFrg-ljtDw4B_HwJUL5SX7u0XHiOw2Wia6ti1nDy2RdXfjmQnh5DND4QnVhGFYNXFS3zboSeEzxvfJq0LalNepssOOp6bMtXi1uHIJn_YgO0Cpdoh6rwZtMfyshw7qzjVtTWWfql0JD0l7PAjwC2ODHuQsO8RRDzJLVzw5YUVoiBSkE_LljkYYx2Ndc2thDBEU5AvSwmPqiGeJVZLySV57PK0iymYKnMrKOjb6Lqr6pO_GHtWdYpBqrxY6qBd0wOHb5h-3jpur2pRA5ibSubMr_qPRex4lyt-0VjIKQWb22mg3dRfjXWL_n6YsJT76kGcE0PGIT8AmyaeDWV8Rio7szbtgVYk19CP2S9DsHu4fBWNLDA-PbTfNfhCVrsU_TySTGvjm7nuFm9NvBzaPAjnFluetij0EIm6E8_MpBHWqBjtUG1GVmI7NGCcmPO3cRcNVuCGgawyU4hBlmUl2SQSZW8smeGh6KhoYqAEMwBmlE3EQZKWjUS4kXaast6fVCqjtWpCqYwYrwAFoDHI-qa6CVtvMmlv2OoIYRCRsKt1ZOQTG6-I90cJmA5XQdk30bLnTJPdsIqoNii5DkCQ-wCp_zbfjqFrgJBblyHGXTY8jpEP-qFpvhLgOGJknE9KGoj42TMfNjkT_Egr9C0XDEqIQuPl79T_ZW1Q-upky8saPcbPyRQbHi61V1og_x3XtDozXb3kCktnfESOv1zKcBONeT2Tu2P7TV2xDf_3lpFQemcozHOTHuL3il2-5codwVoNNmmAJEV9CCJC_bQQrDPaqX90PGwQt_HTXNE7OpOoc5qvDwSlG1aagix0snSi89QDjXPuxDqZwKQsulV8S1SIt-_mJ62xp-UBpOu5ODuUBBjoQGLPovN5c1DmmypdqsO_z4IDh8CToGZXiSRt5hrjrVu3Z8yw0j0P9Fi2ISumOyw7SKQUi2jvoiGYJCncikEaH1FLrA3CvnUaPuFsGml7pEhRVMZ1JTPipWloyRaG8ES-r_HD0kBLm8t7U1MSn6bnOqDhmnBICSM3LjJ4s-Vxk6ZDPXGiT41Z79CIYc-g0oQT4fuljyAfM0wEC3SpAZXWlHptw9Z61ec6a06PL2Y1nfKd3LehmVjg0ykP7VSerzr35n2_lpMPStk76vA2XPna1Q4icmgAt7zjcWjjFuHSTdzMcC-D-CAUOo1KRvn6emR9p7_sz2CJtSwWvVbYhlolPy5oTD8DqEg6Zgp066AW_3wXx77Kbuu5-16phz0OE839zC1yXD_eJ6TR7T6guiMhrxJAarQT9oNCnu_6tEsb_4lBS0SJjiisPL_cuoep1gviSpWDY2JGkx4o0ZCSH0wwG215BGJvvsuMyRWmIVTciv-eY8meJ1DA_HCTYakPjRHFce5fIr4drAgeZOBGBCzi8nfTk8T5j1TAc22_Bqtc7afilL_J7SAcom1nFMFBKHWXW8JKJtF3_q8LlgSdvNw9KEUDPGnaj-njEsNmceCMhChrV8jyaKX-y3kgj9SAIgEqQQXmEWU_CQEvegF5oVlAuj1AIO3s0xk54MLKTngj9uOq_BcXCTDZeY7W2jfMmbIBhKn_gN_a7--WhVO3hjZzgF6qvK6kDUh28L28uIn-P9XBtd_jKnmnSNf3cJ7taUxRARRN0p8LpiCHZvVb8yqqHAGAG-3bLJx1cjugHzPKhRkW_gZCatbaxMKkNVfk_03u020B-1kUtlWXvO_cTs60uJZD3MlDw2A_VFsb1cAl5GpmjyLhcbAuZouRAUVpA2MZDcTql4HH43Yi2lolUxQlm73fvJ-A9MM6nP1M0Urg6bZyXTYSAj_xaQ.WK3yjFBy9dr-ntkI9sxSz4zrBWrxaS8HsG3liINcUQU', + options: { + version: '0.0.2', + key: masterKeyExample as any, + }, + json: { + credentials: [ + { + recovery_id: 'jwt', + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialPayloadEncoded))).toString(), + }, + { + recovery_id: "anoncred", + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialAnoncreds.toStorable().credentialData))).toString(), + } + ], + dids: [ + { did: hostDID.toString(), alias: undefined }, + { did: targetDID.toString(), alias: undefined } + ], + did_pairs: [ + { + alias: pairAlias, + holder: hostDID.toString(), + recipient: targetDID.toString() + } + ], + keys: [ + { + recovery_id: secpPrivateKey.recoveryId, + key: 'eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJrVXFTZTM2SXFFaElueXhzd2tCR21hYjlLRFBXRURFdFlEU0RQTGhSLUlnIiwieSI6IkRKOWxoTDZwVW1FQlJYM3RubzdUQUVBMmJuR0hKRVpjQUd6YkNRZ0NXSnciLCJkIjoiTExXOHZXdmxpTFRIc1c1VVlveDVWR3RwczRzT3JyRV9yWTBIZHFIQXdOMCJ9', + index: undefined, + did: hostDID.toString() + } + ], + version: '0.0.2' } - ], - mediators: [ - { - holder_did: mediator.hostDID.toString(), - mediator_did: mediator.mediatorDID.toString(), - routing_did: mediator.routingDID.toString(), + }, + { + title: 'Version 0.0.2 Provided PK & Compressed', + jwe: 'eyJ0eXAiOiJhcHBsaWNhdGlvbi9kaWRjb21tLWVuY3J5cHRlZCtqc29uIiwiYWxnIjoiRUNESC1FUytBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiYXB2IjoiVk5BTmhuZFl6dmdXdkVhRjlZNHllNVNYRXJCLXZSZkRTRjhfX0o2ZlVUTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiLVRRRFd2eEluMXJFekYtYlluS0w1dGV3ckt4YWstUm5VR1p3OVB2MEZXRSJ9fQ.7iuRcPhTbXwSuzc0GBFdqqk_7Uk3DSdOjEpafZXk6Mezmd2NG3enE7Gds3yhrHr18bHO4lxZyqBc3hFxO1_I3CFGR0vg3rx4.a4Ves8o38-ev_EgPsl0AIQ.QWoExlVXfw-TfOGM_SsQ8yI2wnldqDY4pGjevdaiHlo0y4ghuHAuOgTT9bfPerlFQsR5mremhxznYHlpHxz29Z0XnBLzz7ZAjd_paii5rbBZBxwENa6ullDGpHykHJwnCQXR0sJSWCtMZsqy7HjJgwQLI7L9Yxhet0bqyVVLlMwzH4vZh2naku0jXirKkLmN8V_kMWz2FotHgcffuBYVSqnZSgMDA4EpxobOEOPobweOU509qsz8GXIP1nr1vIbGD5mfZS40D-8qLKiUor81AzCLuTiTQYrjPEhTKHqMk36ig5DX0gdhU526Pi_nzaRqsbbsFbdlV6CnllcSVFmR4oClCrsKSMK0-iNAgzR4YWXc_CPE_IHfI9yeqD015zmBjp2D1gXmYAElg6oICvUQe2Afc2HIoccT2ft14cHb4YewhZ1gPZFAlDwD_-1lAc4qeLBK3r3o4UL0NY7XYxotOnw-JvXrOYYVoeAwloPsgApJ8pRNnUY_zma5cV1VzNoHRAGi1Inrw2-Ftgpr2LsAi2SVaNaKXoe_MefSHvLYN_LlkwCpy-D5VrF6uvUzeaQ_ziWHb-j0Bz6tylVaRK80pO-Y2T7MvWKM3JRoe3M-nhYzfLSdReg0YQk_NOrZKh-A6uTVVSX5EPilvyVFzQVdg-A6Q68-msuantEpfLvKFs4BUtEvt-OELRBv9x966gkThtNgvZ94nefDEyhQfIcaCcfa-1jS-WumqkbODyupMg8PtqEG8XiIBhamnkFAwJl9gm6c4EjM4UC3czwhaiOUEkGEYvOJSqL5-5X9A47toVYb_G27pWoxop5Keqpb0quJ64dBxk2W1k_9KCUUfTmHz1OikVkqHeEpForNjpbb0QyaG-MCbHjqXspoiOCSW9UEpgCZ4bQmW8k9K-kScG1HkfDD9UfIFMOw0_KiufXUiVrXipkoZsr4bjwMWFMIA4w7rpVsmtrvXBomf1Zv0_iWGRp7vof5xA_G1WilVz8dXXExkHSBZFbjx9tcaevK67-WLtMW9TGHgjPBrS9Au7S0Ql3yACbOrbSHIZt8hFPl_zLcAP9O4mF4jtWemCXOkLIHiggP6-YbYHkqWaWAvkAeW9LGo_qTVJ6EmmrA6bWMXsmR3L9ikbDUn4chjbgf8Cs50l-iTZuWYQ1jNdJiff6C_7GGHCD-e7vlgE_5rPXZGWWh7UL22hJPYUkocXSP2trYZTSG3wnnBEs3LqkWFH_aIlZEdD2z__QkCui6XTM2izENCCqB_WX_OMPNYHOAEkbv1yoNySDnGeGp963mjP-JSBmqawZrZV7PsbWENrNkBGsnuousJLBD0PoWJqPk6kFH7TaWaw5A3Yb5jXSFCZhRbharjJPKLEuhiI5uRx_owZFFzjSwH4e4zWpUqaTF1mk5wof4RubNCKgRNnrFBsHcI9yW1s5ykaL1XdhyEANR9mlJlxaerkZcCEJhKy21npR1d3JJm8B_zfFTyo7jHWzLMaxjHO_UiuAH_9qSvoK1p3eO0o_7T2-LQiWcTdWwChjSws4G3hz7UwL1HYhhGO0mRu3LcrqjRSp04Lo6vsIOrxxE9QSRGfdCJKWavcM3JMxLjT9DeNB5isvCB-a8r8SH2ZGFPXj9uQ71hsMnmJESK0837JjDn-mQrRgaA-CLReHqb5b-yEhGEjXgNCmRa1PX0hqLn4qNmo0BC6yPzKQL8LZdwj7HFQ6tiaosnzL0ai0mJfmT_oJytTiF0Ly7_H_KLST3XhsVpbzTlzAys2fgsJfhvc-XiDSxmnHwBsI4tWaP4CTWhtnw63MVQsoyToxzfh_yFvO90ku8iZQ0C4l8MSGESUehHXgw3PvAwpCXhqFDgn39fynSjM3LfjLWwAjfwOa_YSumq704eJQvXWHEmmFMN8B4J74PB-p0VnXsIzHUwQKN9XJAnxUjw1jkmPAPtmuTmasn9vA-8OIJopVVmNBUFDJ-CTzJbnn_80PAzNWSYBA2a9YkLYO1X70BkRPxt7Lz7wp61zxRDxykLDket39WXh4L1Ig4ZhWL9AjUrNToKaB7hzPi-Jex_7uSQ9K1kXS6R_kO9htUckQaB-Ts2otx5qXv0IQzMgApDwV53fzCT3L7cOCFvGz9jxeJs3IA3z0jKW5ePnASjTODVc95Il36p6eeRiXpg2vRCcMV-t1P-vszTkdunRHPGkIXZETC9lAwkshPhCVMqQijJBmrh2vVaN32utbsORDr83MZVRROcQdD4uI60dDfrMerfJ20KzQ_MmSN7vekyUEJcI82rOfw7c62gAsc8EhBKmRxCKVx1uf6PKs_1qM17yfYX59HZCZtj_ivsJDrdHGB8tCwyVrYsoTGUZb-jTCWFueT0GJSA6qcXImWKDC2Dlq_X-b1xswITDJRUZ2DoLoH8kli0FLzTL-VZGp6ShXmj77THUUhxGnbMEIWpCd68CwOE5a2nEEFGzuV7OwS_NMxRseExcXOUygypeeaAXNMc-9JVV35e0hzgMl3uyXfHzceIPPapafwt97dewTE69L47VaERk3N0Mhvvvd3NOlV548or2A2BQUmSn-bGYcRLlzmcH2FPlNf1Qgp9oTHZfJv4CSPjhgOc-Rf0LkqwcMZffw3WMRjiJjEzIdVxnr52rK7MBREwncjrrh4GXGIGIJMfBca0R_upwftZgOhfIU8sC7NrmUrsqpKx90jhBqgvhcx-esJXLwdWvrBNsTQcAHpicORE3s_raIldoT24d5fhd3KrNyWI_3p3HN82b3oZes7VuhT076sxANgZEfRh95sLowZx7QkWw8f_LQEMO8pTC3Saqs4DKrEjnkcLh1q2ZzJWh9BLy3J-CXytwS7cEgOcG0iLbWZHbDDAbmAlQz8wAI1RKobXmxeJ8-DKPQrDUfFBwuIjKS-1pN1B7vNFdGiHK7TzS2aNpFiz9TvOh1nka2O-Lxkz8LUyNjH3y5qHeP1BlpLtbvxioTQxckf_LaqVJkFZ850-6zpnVjxOa2JRJgOSimKYYnwZbY3xd5Zj0n2rpuMgGVMK_nWYbxZ3geXAEIifP9pYqtM3WTALlb4iQ4MBWJ4JaSK6BHjfaIORXTGddbRWtLXtoqheJ_Po0CESkFycTI-e-KTokTgou4KvThLjqxOs12RVW_m-Lgf-fIQfZmW1rqgTk8t7oFX0hoM0QTnYnS-BOOKzLn4toh2X9wIFCfA5CKuUqVvVN0i_6yTgMh_PMkuJobZgzI1Zsc4VkkfW-pmrvHGni_q4J_PUJPgi79jTFsaXcrrLbKrIl_xavEToik5Hp-bVVpjbOJrhfJRqWQLYODJN25i4WG-5ePsUsHBLNqiIlW1FH9MzgbdtrhgBvPJUwvCynU-9Nn0UuD8j8zCSvMrmvAga6oLrdXsAE6rWBsuz6H7eTprLLgJoBGIGKJTAXqrq0T26UZuQSfbnI5PezkwpRspWZElXvG9Pe21W5F2ztTHVfN54nfhxgbFDYYX-moIEcfg_govA4JtLmVyrQ6QMcD4su_K9RfaYVgB9DxWg6N0-3cTd-qfMfjtLDl8_d9BDngCWGklRppP827OjkVgZASmCrmz8rF9S7P8H3IAeNhXw3sNzf4Vanh4umEL_3ViMTUdkiY3AFIaL75Vk1nKo-xnUe8S5u6r5mS4Oj8HVJ-TC_q_3uIhsDhnGnXLKdrIBo3MAsh9ClWIcSaowwoH9EBVNuECYI75SNsWhA-TgFR8Luz9XOUTLTUvuF_b4OEtTyIlsbWudMRFmQsENdS_U2dMoNzSKumAUpfYoC2icaoJzJ4-6O_2S1bBx4OSvtu6Wfv4tarN3sCo_sfUcp_g4DFdw93b_iZYSRvW459cw_J1ywKapXElM3s7BqvCN95bIbkVYMjvHJUAmWZS5OK2tYTg4fMVxHnW0CK5GPAXO5CS4O33t7wcKLV3_5Q70yhGtZ6eSqyTfmD4mOJZ9MaHUXMg5AqOSRTe3CO2SMBk9wsv-b8G1s2C9uWZvUvH2wyHfSZcXF4b7NbSU70pgbWNcEProjzMgR9jNZYGAFRDOW2arUj7SwWtLhCDHqNqzHtAQGCBxDZ7j-vYPaScvVWKnoS5E-rDUD2ebK9RibRBPqp3UA9Sf2Rs_moUBTae5aA_lgi9k_-i20GS2Gt-qKAQYZxaaYVkCAZt_3KZaa5zfqKnxmHWmoZcoFSP-5gxSe3QGZBnbqPtNiGBckVoGM4Fg0gGrWe-EbVc6OtMwB9sAqd_c-vujWQwPVrsKqXywgW-pq3wOWRYa-JaMH0O-YfAt3Wwszw5GNHSI2kzZmPRGMon219znsvMfrfNHv5TcqrXx86DHKsmBQMNq3wBu2D6EHkOTQKH8e7Mblc2_KU1YkTKqejz-nkQqHlRcUv0CGhmr0yfAoCvMrbjNrIeOIyu912spa6KZzURcZycnVPzOcB3YGZfV43OV9scU8pf0V8vJlD6cv5oqxZtwl2FHdkW18hxifex-cm7vqkJi9jvL7v8FNFWa-qOqIyvwmA7eVT29objwJ-iEUlb5VG3I0rsTCUoSjVs54GOob_TRMwDfxA7VNcdcOLA8DzTrUhZEYEdt3WNo7RnrT9SxZr4UDkceurMeLENRxRaBthlvrPG79Ls-Tw91z2KEaoCsmHMQaHtPboH5W5z8Rlxd0KvoAz4p0rl3JH6IdLSGtnswDRNbH0pL0pPrl-83aeoJSZ7Idr2XkRChY9oFfyjHJRvzHYmy8LpkUIwm_tXZNUqA-6MwOH4TiBrZZdM84lfqPkfR0qDYSij2eqTMiOSBKx-qLEqgNfBsZQYMELTThxENUV7J7w_sxZgmhob3jinqJSZ6XCvgGezpyqQjne5uh0CxtpSvDEV4gjjB7p-jinLV0fTaIwLhnMsw3pjRDaLZU-_OXntYB673n-Bo8zF3DokkBDN0hFvzBBXjQs-YCkTFzSGabMNW9jlE-86z9lTQZtmhoy8lc_3w2YuITO0Mo1YgX1EY2oc1DdnfnCUI-P6XNQvx2XMw7hXzDk8VEwZI-tv-RkC4PdnB-fmOTKnsqU-QQGB5HhDBOENaR6rtbIaxblpU4AlDASQYP9xCz4t5Bo9fAbQFm2aUOOZqMoh3yiEpI7Z7dDCNGKyPCiNrhWYjopoXUY2DLeKUgyBZXAI7mmnjz4-mmmv-2IRRlMOK893kPiTu_XHT9BhjUrLD2avI8XNW6N7obvGmKXES-6voumJibeXtEnl9svLAs6y71auYB97lmirY-eB9SM8cO-_ZLhykekfJiPUN5ALkBlsB1YMeCj3ukFqmEbIPMgVaA8X1hO6VVO8BaaIA1_aajO6TjSnlzKzRn1slN_ie-EZv1y2E-alp34BBGxvsRXlWx_r0yv7o8CY_qfOcKrmnyMfnXUdeGWOs6R5sHyF1rR110LFJMRg4xzIIUV4Pl03UKvP0J1fH2aFG4rF7EgMsW1cFgindkKuQxT2WCjY8nbRzI15X98kn8u21GHO04GKSfW6YmthiO-G4wDjQVOYwaTa7teB8C8dlZEGbDtPMna0l0e65JkvaTE-OQQqqAyTbRP34VtlEK6KuBn0nCcz88VB3safEDk8AQX7KpdIieBkZ9swGcKDvnPKkgifMbpmzF2VqLYMW9-4LzuoIz5A2qIVEQ4c1UBDxaHZUDzEBp4WPtzwyzUYRRjWgsTVV8tQGGzzVY2qMJ3oig31SNnKVYzADwH6L0V0n77je4BcAVfWd63Fm5uL0Xwekd8uDYWC154NAcg0UtMRVmyrKAiH4ooBBfrwPLBSM-KmnkC3dvCU9fGEodNEDaxaVFddWggm4t_7PHcGFUrbyGCMxEIXRZKHXQ9WR0X7WWnL-49asJjeE2erWlPFXkaG-1Ay98k76T4fPURjDO8aHDCjcXg1LqUtSI74FOD6JAAIzUSMXFOOaYRApRe6npPBzB79ulH9XaWJKQ84f2SSTQ3VyF5jNZg3AbZ1zpl3ltHCPSUsbKE2vmorFp-UELg2Qpzmk6GhhFMBAhMaVEas643QqYL4CelbEXPCLTU553fUs9jcaA6Jv5ukzzrhuJKFpM2-QJljfJr19DWunxv7HY4CwBc7kw6AFMONXkwqh_BobsFuJwepTN4ioE7KKu93fXZ25PtpzufYVz0-UpGSo7I27W50MoORxwj-QgD9NpKbb1vRVHdHiACCbmhm8-ycuXGFGXGEnWOQnPZZ4qS3oNgXogsYJ2yGDuechvP3cgzm1cv4V5j1uqujKJ3ScvIGAGmpns6l-OKIfHWeTQttpY_sThH3HnTyMQkCYrmPy8b3BgKkYh0XcINTx4dvC_YmePQNRVjR3woCUG6GNar_2UBvHzZf-Ev-_CVfTyG8lSxIA9lXsSNWCKqqjZEEGNI6xb8_OWDovouIkt_FW4i1u3f_vX7K6Org1FIeEVhmEULVoTzQ-Cx8yFQ_AaGENQQBBvc0qa7_iOHdjD_uRPOcDPKsLY5NHOzfAz2MKsSpHJJFp3_PBta1ufscPGa7dqynbQ6rY-tWPxb9RPrLEna1ov8CzEb6T55L8G47o5UXsYgtdhmJwyG1HgUfRXXViEtmntrwXvaV9wMK7XiuiehFph-dFbMeSKNVScoX7rFzmnkQzHg0DJD8d1Q3c1m0PDdnumy5yz2JP_Smow4nMBb-peMH-aBlaKWev13ab4G4L2gInrbfdXkjsmZFpg6tRZLWimnib-QjwC2lwF4DsydQjnGA82VQzlNFs73-xBZu6ZVmKyElgPklc08oKXM81ll_SJaB1rJ1ZVtXxkSaKig8371lQRVm-W8ve3zi5_3cjdG4JsniKS5rTpzlJn3gvv15a4PgncrdJVscrnL0ZrJHd377gW6sJOXmttL9z4aFiMp01Ma36ZHmsmcaQblNlJ09Nob_4GHwTEEFUx6XYmP9644aZnHItE0RPmgLfzmOm6sg2XsKyLW5CI7DHptpomgwW7rL6OvgdtuKDUH8RlcDqPLcDRff5sPGAq28aGfU9KZIp5ngML2aaXXOe7QDtTVOg1DkcTdq6ijE5xbATU1IOb82Vi3LolnLkIdmLJVIqB5pcYzEtd5ZbfMasqDilSDD-4w9UcSJbQKG40syjQ6OmB-PgOO9_hwRHXZQrC5Aw15d8P5uMrdp0YXKApcYcUrlLWonzlVKFE-7gNJBBjjrzCCDPH3Z-i2VQsLYOvafPV1NGd7AyhyWoL6nO7EZJuc32ARl28lfl6pxjJSG4HD82-My4elh4RDwWw0GoUv4PIVs-TJEDSl8h4EQV841cWiPyGQVrzqfxkRbyOkj-02o-_oJPyfBgihOuzKEIwamjzbaEyMmNuqnmdl1ZahUc5r3Q-LqbpojdTc-Zx6nD6U0LdZD6zM-5jp1FXgLLE63mVfPoTRbEt77WiQayVvSgOfEdo3mpPpogOAHvVGezJUYL7XTQSlrYsGbFdj4T6XQp7zo1-CrhQatmSOs4R0OhEENyXMWMGHVSAxwtUg7kQ8AXmXLS2cUEvwOOWxogY533L9ZoQLXju7Co-qTHPLNBDeAF8QagYLGncTGUemb9s-rQxtaGLMuH7s4Ji6WpZtjWVgR5Wm2HzmvqZAaJqkatT1Az8yJyYduexdXLeSjG-QHBT5Uc-o3L8v3-uaJ8qVW2WirV0haD3N5nfdIlfKNrShV5PKUJ0Mn64RDgpa5wVGLHRLE8vJibOXHTOqwaInYPcp2yfBUSd0JVhRN1rLu3yPAx7cjlSE0qmzCWpiiAVEr4DflatVB6pMwh9XoHoWS6FbjRIWzAm1G7QafWCCEQf4uY3LjOQXMwReyUnQEDmy3RPIxoa9AXWCkgOO5tX0HRFigIwnORYRLksYNma0puo3nIubLS_qyJr2oh9qT8eVdQ6Rozwy4rKics_6K72OPEwvuvtFOanhl5BQ9EcW789QyiRqmtqeg-vWyNRM19YTdrGa_Gz0efMve50pTTkmbZevtflFrkk-gdL2hTgB9nSbJg0CzzwRA93C459erIdquF9uZcqA4IOiC-echms-fulQTKTCSGT9RRvV1nDO4tnx-tV7dND0Z1Q1Jon-AjiKz3_qpebp-bGJ-QK2AmWjkrBDkS7V849sm82eT9GpXxhe_55-Y0sbjxjbb5uSGm_M2kEHRRiL7iwvl49vy3Ui5zz3myelaK4988bFBk6bOybJ_Sizukeygw5xZN1FXnPTrwpmR11mrmLt0oMrNPRiXzmDXtuRn70ZueF9PeAo72t45iflvI4LgFZeF2iY22AOD_0B_dwbE_HpGqfOGboJgsy7MSOLFI0ioALLwl00u7MRxt_vF0fM9xmv91X90kWHlP2x05o1c4uF85Vz7ExpjTAZ5NuKVOZtFV_svCiyqtJdO4_uZiyNkU6Zj-nsPmI9mXz8O2WItrmpAyyepcGYokKuOy2SaKPHYzqme4Mw7TZVMHOIjdpMGRm1TSTBjRD9CxgDQ0qJFkEqTPUfK4fuIZ6r0dUcOj6-XZ0Vgs0QBQis4FpnKWC5185A3dtc8O3fGUAqGUrXNWQYCZKKFzM1IPNoahL50eOz2U9UuRZzMfY6ZopcVqAm_2qXdpPrbmPaeb8BsXSN88jPgTXrUYwfuF-enTNiQdIG_V2vSxAeQ-WLeLPBzT7G9qFrY6spBS7_LFebwAJErDo7qZ80N1Ktpg-cE2VKQbkxlj6x_CRdEC1DrU61mqpbS68TAR2zJtCIBKAFg10UhUkwtEY6fY_AO0XqIQQ3DdI1LQplipkdrGx4No_BSjoMsum--I_QQQ6MKVOLMM360fHRG_8kaHu-aPPmvrF8And1GD6ueOJrE37ygKTkfS4vm-tfq4aDZ5n0XpZHJdgmadtcRdOZTCre52O8BSlgvhsBjbU74V4xVpZ3DoNJYo4-XhFpd-iQvcKFvv6t3BXPzZPHhPQ-x5L0mBZoYnYo8Pgyey-c-Z3-SEYkldMAkfop-zCS6gsSqfaHXH5bZdjJJzhKT243Aeqd-9tAL--NIZvmQTj5r126XGPJZTYn-6UDrNGQgHE0yidzsTKxnZPok_PrUY_JY8WUCYhhMFy-bpj7OY0uH1E6lqpQAEB_Epq1QyigHhz5xtYFCG2wRobeEFGQrD25u-kq_Gh_i4GazvRj6hf1hB2YAQOGnLb6anKmEzjhtwSR9llay8VC_LI2evqt07yrXR5jM1QfqIK6dkpoO9VdgFk0OxzRKKmPeu9F5OlvgBWcA0iOeN80lns5UlKSvrutyzEH6-UtzUZHtgEkXOBGx8_LzM-6iMQaJeXLaiPmghip65EmeeiNKTnAIzi0f02t6nnDdDAuLxrHVUrYzlw.xT8HsswRQHNL5w90aNfQADD5s_rzFyaDQyQlpz8u370', + options: { + version: '0.0.2', + key: masterKeyExample as any, + compress: true + }, + json: { + credentials: [ + { + recovery_id: 'jwt', + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialPayloadEncoded))).toString(), + }, + { + recovery_id: "anoncred", + data: Buffer.from(base64url.baseEncode(Buffer.from(credentialAnoncreds.toStorable().credentialData))).toString(), + } + ], + dids: [ + { did: hostDID.toString(), alias: undefined }, + { did: targetDID.toString(), alias: undefined } + ], + did_pairs: [ + { + alias: pairAlias, + holder: hostDID.toString(), + recipient: targetDID.toString() + } + ], + keys: [ + { + recovery_id: secpPrivateKey.recoveryId, + key: 'eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJrVXFTZTM2SXFFaElueXhzd2tCR21hYjlLRFBXRURFdFlEU0RQTGhSLUlnIiwieSI6IkRKOWxoTDZwVW1FQlJYM3RubzdUQUVBMmJuR0hKRVpjQUd6YkNRZ0NXSnciLCJkIjoiTExXOHZXdmxpTFRIc1c1VVlveDVWR3RwczRzT3JyRV9yWTBIZHFIQXdOMCJ9', + index: undefined, + did: hostDID.toString() + } + ], + version: '0.0.2' } - ], - messages: [ - "eyJpZCI6IjMwZmIyNTU1LWM5YjgtNDExZC1hOTY2LTg0ZmMwOGJiMWVmZCIsImJvZHkiOnsiY29udGVudCI6IlRlc3QgTWVzc2FnZSJ9LCJwaXVyaSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvYmFzaWNtZXNzYWdlLzIuMC9tZXNzYWdlIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNieXVFOTVXQUpZaFFhcEhSVnVSbTZLTEdiYjc5ZjVVWGlhWVRBWXBVYlpnZC5WejZNa2s0U1l6TER0WkpudVJOMlZKNGtuWmN1SlU0bzVDYjlVVURodkF0YVVaQ3g3LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1ScFpEcHdaV1Z5T2pJdVJYbzJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUzVXZWpaTmEyaG9NV1UxUTBWWldYRTJTa0pWWTFSYU5rTndNbkpoYmtOWFVuSjJOMWxoZUROTVpUUk9OVGxTTm1Sa0xsTmxlVW93U1dwdmFWcEhNR2xNUTBwNlNXcHdOMGx1Vm5saFUwazJTVzFvTUdSSVFucFBhVGgyWXpKc01FeFlRbmxoV0U1MFRGY3hiRnBIYkdoa1J6bDVURzFHTUZsWGVHaGpTRXB3WXpJd2RXRlhPR2xNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREU1TGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwd04wbHVWbmxoVTBrMlNXNWtlbU42YjNaTU0wNXdaRU14ZDJOdGJIcGlVekYwV2xkU2NGbFlVblpqYVRWb1pFZEdjMWxZUW5saFdFNTBURzFzZGt3elpIcEphWGRwV1ZOSk5sZDVTbXRoVjFKcVlqSXhkRXd6V1hsSmJERTVabEVpTENKeUlqcGJYU3dpWVNJNlcxMTlmUSIsInRvIjoiZGlkOnBlZXI6Mi5FejZMU21Zalk1Y25ISkVEM1J6YkJrb1F1RzVLd3Q5dEM3WHIyNVlCeFRmQ2FoTm9MLlZ6Nk1rdDVCNVR3d2NVaHFTVFZNWFUzV3B6cHhSTXF6WEd5VTkxeUNpZHZ2b1BvTVYuU2V5SnlJanBiWFN3aWN5STZJbVJwWkRwd1pXVnlPakl1UlhvMlRGTm5hSGRUUlRRek4zZHVSRVV4Y0hReldEWm9Wa1JWVVhwVGFuTkllbWx1Y0ZneldFWjJUV3BTUVcwM2VTNVdlalpOYTJob01XVTFRMFZaV1hFMlNrSlZZMVJhTmtOd01uSmhia05YVW5KMk4xbGhlRE5NWlRST05UbFNObVJrTGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwdmFXRklVakJqU0UwMlRIazVlbUZZVVhSalNFcHdZekl3ZEdKWFZtdGhWMFl3WWpOSmRWbFlVbWhpUjBaM1kyMXNlbUpUTlhCaWVVbHpTVzVKYVU5c2RHUk1RMHBvU1dwd1lrbHRVbkJhUjA1MllsY3dkbVJxU1dsWVdEQWlMQ0poSWpwYlhTd2lkQ0k2SW1SdEluMCIsImNyZWF0ZWRUaW1lIjoxNzA5NjMzOTczNDM4LCJleHBpcmVzVGltZSI6MTcwOTYzNDA1OTgzOCwiYXR0YWNobWVudHMiOltdLCJhY2siOltdLCJkaXJlY3Rpb24iOjEsImV4dHJhSGVhZGVycyI6e319" - ], - link_secret: linkSecret.secret, -}; + }, +]; \ No newline at end of file diff --git a/tests/pluto/Pluto.Backup.test.ts b/tests/pluto/Pluto.Backup.test.ts index afed2abda..b5f19e623 100644 --- a/tests/pluto/Pluto.Backup.test.ts +++ b/tests/pluto/Pluto.Backup.test.ts @@ -1,4 +1,4 @@ -import { vi, describe, it, expect, test, beforeEach, afterEach } from 'vitest'; +import { describe, it, expect, test, beforeEach } from 'vitest'; import chai from "chai"; import chaiAsPromised from "chai-as-promised"; @@ -10,6 +10,7 @@ import { mockPluto } from "../fixtures/inmemory/factory"; import { AnonCredsCredential, JWTCredential } from "../../src"; import { base64url } from 'multiformats/bases/base64'; + describe("Pluto", () => { let instance: Domain.Pluto; @@ -18,380 +19,404 @@ describe("Pluto", () => { await instance.start(); }); - describe("Backup", () => { - test("default - no values", async () => { - const result = await instance.backup(); - - expect(result).not.to.be.null; - expect(result).to.have.property("credentials").to.be.an("array").to.have.length(0); - expect(result).to.have.property("dids").to.be.an("array").to.have.length(0); - expect(result).to.have.property("did_pairs").to.be.an("array").to.have.length(0); - expect(result).to.have.property("keys").to.be.an("array").to.have.length(0); - expect(result).to.have.property("messages").to.be.an("array").to.have.length(0); - expect(result).to.have.property("link_secret").to.be.undefined; - }); - - test("credential - JWT", async () => { - await instance.storeCredential(Fixtures.Backup.credentialJWT); - // await instance.storeCredential(Fixtures.Backup.credentialAnoncreds); - - const result = await instance.backup(); - - expect(result.credentials).to.be.an("array").to.have.length(1); - expect(result.credentials[0]).to.have.property("recovery_id", "jwt"); - const expectedData = Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Backup.credentialJWT.id))).toString(); - expect(result.credentials[0]).to.have.property("data", expectedData); - }); - - test("dids + did_pairs", async () => { - await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); - - const result = await instance.backup(); - - expect(result.dids).to.be.an("array").to.have.length(2); - expect(result.dids).to.include.deep.members([ - { did: Fixtures.Backup.hostDID.toString(), alias: undefined }, - { did: Fixtures.Backup.targetDID.toString(), alias: undefined }, - ]); - - expect(result.did_pairs).to.be.an("array").to.have.length(1); - expect(result.did_pairs).to.include.deep.members([ - { - alias: Fixtures.Backup.pairAlias, - holder: Fixtures.Backup.hostDID.toString(), - recipient: Fixtures.Backup.targetDID.toString(), - }, - ]); - }); - - test("keys", async () => { - // await instance.storePrivateKey() - await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); - - const result = await instance.backup(); - - expect(result.keys).to.be.an("array").to.have.length(1); - - const expectedKey = Buffer.from( - - base64url.baseEncode( - Buffer.from( - JSON.stringify(Fixtures.Backup.peerDIDKeys[0].to.JWK()) - ) - ) - - ).toString(); - expect(result.keys[0]).to.have.property("key", expectedKey); - expect(result.keys[0]).to.have.property("index", Fixtures.Backup.peerDIDKeys[0].index); - expect(result.keys[0]).to.have.property("did", Fixtures.Backup.hostDID.toString()); - }); - - test("link_secret", async () => { - await instance.storeLinkSecret(Fixtures.Backup.linkSecret); - - const result = await instance.backup(); - - expect(result.link_secret).to.be.a("string"); - expect(result.link_secret).to.eql(Fixtures.Backup.linkSecret.secret); - }); - - test("mediators", async () => { - await instance.storeMediator(Fixtures.Backup.mediator); - const result = await instance.backup(); - - expect(result.mediators).to.be.an("array").to.have.length(1); - }); - - test("messages", async () => { - await instance.storeMessage(Fixtures.Backup.message); - const result = await instance.backup(); - - expect(result.messages).to.be.an("array").to.have.length(1); - }); - }); - - describe("Restore", () => { - test("credentials - JWT", async () => { - await instance.restore({ - credentials: [ - { - recovery_id: 'jwt', - data: Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Credentials.JWT.credentialPayloadEncoded))).toString(), - }, - ], - dids: [], - did_pairs: [], - keys: [], - mediators: [], - messages: [], - link_secret: undefined, + Fixtures.Backup.backups.forEach(backupFixture => { + const version = backupFixture.json.version; + + describe(`Backup ${backupFixture.title}`, () => { + test("default - no values", async () => { + const result = await instance.backup(backupFixture.json.version); + + expect(result).not.to.be.null; + expect(result).to.have.property("credentials").to.be.an("array").to.have.length(0); + expect(result).to.have.property("dids").to.be.an("array").to.have.length(0); + expect(result).to.have.property("did_pairs").to.be.an("array").to.have.length(0); + if (version == "0.0.1") { + expect(result).to.have.property("keys").to.be.an("array").to.have.length(0); + expect(result).to.have.property("messages").to.be.an("array").to.have.length(0); + expect(result).to.have.property("link_secret").to.be.undefined; + } }); - const result = await instance.getAllCredentials(); - expect(result).to.be.an("array").to.have.length(1); - const sut = result[0] as JWTCredential; - expect(sut.audience).to.eq(Fixtures.Backup.credentialJWT.audience); - expect(sut.claims).to.deep.eq(Fixtures.Backup.credentialJWT.claims); - expect(sut.context).to.deep.eq(Fixtures.Backup.credentialJWT.context); - expect(sut.credentialSchema).to.deep.eq(Fixtures.Backup.credentialJWT.credentialSchema); - expect(sut.credentialStatus).to.deep.eq(Fixtures.Backup.credentialJWT.credentialStatus); - expect(sut.credentialSubject).to.deep.eq(Fixtures.Backup.credentialJWT.credentialSubject); - expect(sut.credentialType).to.eq(Fixtures.Backup.credentialJWT.credentialType); - expect(sut.evidence).to.deep.eq(Fixtures.Backup.credentialJWT.evidence); - expect(sut.expirationDate).to.eq(Fixtures.Backup.credentialJWT.expirationDate); - expect(sut.id).to.eq(Fixtures.Backup.credentialJWT.id); - expect(sut.issuanceDate).to.eq(Fixtures.Backup.credentialJWT.issuanceDate); - expect(sut.issuer).to.eq(Fixtures.Backup.credentialJWT.issuer); - expect(sut.properties).to.deep.eq(Fixtures.Backup.credentialJWT.properties); - expect(sut.recoveryId).to.eq(Fixtures.Backup.credentialJWT.recoveryId); - expect(sut.refreshService).to.eq(Fixtures.Backup.credentialJWT.refreshService); - expect(sut.subject).to.eq(Fixtures.Backup.credentialJWT.subject); - expect(sut.termsOfUse).to.deep.eq(Fixtures.Backup.credentialJWT.termsOfUse); - expect(sut.type).to.deep.eq(Fixtures.Backup.credentialJWT.type); - }); - - test("credentials - Anoncreds", async () => { - await instance.restore({ - credentials: [ - { - recovery_id: "anoncred", - data: Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Backup.credentialAnoncreds.toStorable().credentialData))).toString() - }, - ], - dids: [], - did_pairs: [], - keys: [], - mediators: [], - messages: [], - link_secret: undefined, - }); + test("credential - JWT", async () => { + await instance.storeCredential(Fixtures.Backup.credentialJWT); + // await instance.storeCredential(Fixtures.Backup.credentialAnoncreds); - const result = await instance.getAllCredentials(); - expect(result).to.be.an("array").to.have.length(1); - const sut = result[0] as AnonCredsCredential; - expect(sut.claims).to.deep.eq(Fixtures.Backup.credentialAnoncreds.claims); - expect(sut.credentialDefinitionId).to.eq(Fixtures.Backup.credentialAnoncreds.credentialDefinitionId); - expect(sut.credentialType).to.eq(Fixtures.Backup.credentialAnoncreds.credentialType); - expect(sut.id).to.eq(Fixtures.Backup.credentialAnoncreds.id); - expect(sut.issuer).to.eq(Fixtures.Backup.credentialAnoncreds.issuer); - expect(sut.properties).to.deep.eq(Fixtures.Backup.credentialAnoncreds.properties); - expect(sut.recoveryId).to.eq(Fixtures.Backup.credentialAnoncreds.recoveryId); - expect(sut.schemaId).to.eq(Fixtures.Backup.credentialAnoncreds.schemaId); - expect(sut.subject).to.eq(Fixtures.Backup.credentialAnoncreds.subject); - }); + const result = await instance.backup(version); - test("dids", async () => { - await instance.restore({ - credentials: [], - dids: [ - { did: Fixtures.Backup.hostDID.toString() }, - ], - did_pairs: [], - keys: [], - mediators: [], - messages: [], - link_secret: undefined, + expect(result.credentials).to.be.an("array").to.have.length(1); + expect(result.credentials[0]).to.have.property("recovery_id", "jwt"); + const expectedData = Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Backup.credentialJWT.id))).toString(); + expect(result.credentials[0]).to.have.property("data", expectedData); }); - const result = await (instance as any).Repositories.DIDs.get(); + test("dids + did_pairs", async () => { + await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); - expect(result).to.be.an("array").to.have.length(1); - expect(result[0].toString()).to.eq(Fixtures.Backup.hostDID.toString()); - }); + const result = await instance.backup(version); + + expect(result.dids).to.be.an("array").to.have.length(2); + expect(result.dids).to.include.deep.members([ + { did: Fixtures.Backup.hostDID.toString(), alias: undefined }, + { did: Fixtures.Backup.targetDID.toString(), alias: undefined }, + ]); - test("did_pairs", async () => { - const name = "test-did-pairs"; - await instance.restore({ - credentials: [], - dids: [], - did_pairs: [ + expect(result.did_pairs).to.be.an("array").to.have.length(1); + expect(result.did_pairs).to.include.deep.members([ { - alias: name, + alias: Fixtures.Backup.pairAlias, holder: Fixtures.Backup.hostDID.toString(), recipient: Fixtures.Backup.targetDID.toString(), - } - ], - keys: [], - mediators: [], - messages: [], - link_secret: undefined, + }, + ]); }); - const result = await instance.getAllDidPairs(); + test("keys", async () => { + // await instance.storePrivateKey() + await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); - expect(result).to.be.an("array").to.have.length(1); - expect(result[0].host.toString()).to.eq(Fixtures.Backup.hostDID.toString()); - expect(result[0].receiver.toString()).to.eq(Fixtures.Backup.targetDID.toString()); - expect(result[0].name).to.eq(name); - }); + const result = await instance.backup(version); - test("keys", async () => { - await instance.restore({ - credentials: [], - dids: [], - did_pairs: [], - keys: [ - { - recovery_id: Fixtures.Backup.secpPrivateKey.recoveryId, - key: Buffer.from(base64url.baseEncode(Buffer.from(JSON.stringify(Fixtures.Backup.secpPrivateKey.to.JWK())))).toString(), - } - ], - mediators: [], - messages: [], - link_secret: undefined, - }); + expect(result.keys).to.be.an("array").to.have.length(1); - const result = await (instance as any).Repositories.Keys.get(); + const expectedKey = Buffer.from( - expect(result).to.be.an("array").to.have.length(1); - expect(result[0].curve).to.eq(Fixtures.Backup.secpPrivateKey.curve); - expect(result[0].index).to.eq(Fixtures.Backup.secpPrivateKey.index); - expect(result[0].keySpecification).to.deep.eq(Fixtures.Backup.secpPrivateKey.keySpecification); - expect(result[0].raw).to.deep.eq(Fixtures.Backup.secpPrivateKey.raw); - expect(result[0].recoveryId).to.eq(Fixtures.Backup.secpPrivateKey.recoveryId); - expect(result[0].size).to.eq(Fixtures.Backup.secpPrivateKey.size); - expect(result[0].type).to.eq(Fixtures.Backup.secpPrivateKey.type); - }); + base64url.baseEncode( + Buffer.from( + JSON.stringify(Fixtures.Backup.peerDIDKeys[0].to.JWK()) + ) + ) - test("messages", async () => { - await instance.restore({ - credentials: [], - dids: [], - did_pairs: [], - keys: [], - mediators: [], - messages: [ - "eyJpZCI6IjMwZmIyNTU1LWM5YjgtNDExZC1hOTY2LTg0ZmMwOGJiMWVmZCIsImJvZHkiOnsiY29udGVudCI6IlRlc3QgTWVzc2FnZSJ9LCJwaXVyaSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvYmFzaWNtZXNzYWdlLzIuMC9tZXNzYWdlIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNieXVFOTVXQUpZaFFhcEhSVnVSbTZLTEdiYjc5ZjVVWGlhWVRBWXBVYlpnZC5WejZNa2s0U1l6TER0WkpudVJOMlZKNGtuWmN1SlU0bzVDYjlVVURodkF0YVVaQ3g3LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1ScFpEcHdaV1Z5T2pJdVJYbzJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUzVXZWpaTmEyaG9NV1UxUTBWWldYRTJTa0pWWTFSYU5rTndNbkpoYmtOWFVuSjJOMWxoZUROTVpUUk9OVGxTTm1Sa0xsTmxlVW93U1dwdmFWcEhNR2xNUTBwNlNXcHdOMGx1Vm5saFUwazJTVzFvTUdSSVFucFBhVGgyWXpKc01FeFlRbmxoV0U1MFRGY3hiRnBIYkdoa1J6bDVURzFHTUZsWGVHaGpTRXB3WXpJd2RXRlhPR2xNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREU1TGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwd04wbHVWbmxoVTBrMlNXNWtlbU42YjNaTU0wNXdaRU14ZDJOdGJIcGlVekYwV2xkU2NGbFlVblpqYVRWb1pFZEdjMWxZUW5saFdFNTBURzFzZGt3elpIcEphWGRwV1ZOSk5sZDVTbXRoVjFKcVlqSXhkRXd6V1hsSmJERTVabEVpTENKeUlqcGJYU3dpWVNJNlcxMTlmUSIsInRvIjoiZGlkOnBlZXI6Mi5FejZMU21Zalk1Y25ISkVEM1J6YkJrb1F1RzVLd3Q5dEM3WHIyNVlCeFRmQ2FoTm9MLlZ6Nk1rdDVCNVR3d2NVaHFTVFZNWFUzV3B6cHhSTXF6WEd5VTkxeUNpZHZ2b1BvTVYuU2V5SnlJanBiWFN3aWN5STZJbVJwWkRwd1pXVnlPakl1UlhvMlRGTm5hSGRUUlRRek4zZHVSRVV4Y0hReldEWm9Wa1JWVVhwVGFuTkllbWx1Y0ZneldFWjJUV3BTUVcwM2VTNVdlalpOYTJob01XVTFRMFZaV1hFMlNrSlZZMVJhTmtOd01uSmhia05YVW5KMk4xbGhlRE5NWlRST05UbFNObVJrTGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwdmFXRklVakJqU0UwMlRIazVlbUZZVVhSalNFcHdZekl3ZEdKWFZtdGhWMFl3WWpOSmRWbFlVbWhpUjBaM1kyMXNlbUpUTlhCaWVVbHpTVzVKYVU5c2RHUk1RMHBvU1dwd1lrbHRVbkJhUjA1MllsY3dkbVJxU1dsWVdEQWlMQ0poSWpwYlhTd2lkQ0k2SW1SdEluMCIsImNyZWF0ZWRUaW1lIjoxNzA5NjMzOTczNDM4LCJleHBpcmVzVGltZSI6MTcwOTYzMzk3MzQzODg2NDAwLCJhdHRhY2htZW50cyI6W10sImFjayI6W10sImRpcmVjdGlvbiI6MSwiZXh0cmFIZWFkZXJzIjp7fX0" - ], - link_secret: undefined, + ).toString(); + expect(result.keys[0]).to.have.property("key", expectedKey); + expect(result.keys[0]).to.have.property("index", Fixtures.Backup.peerDIDKeys[0].index); + expect(result.keys[0]).to.have.property("did", Fixtures.Backup.hostDID.toString()); }); + if (version == "0.0.1") { + test("link_secret", async () => { + await instance.storeLinkSecret(Fixtures.Backup.linkSecret); + + const result = await instance.backup(version); + if (result.version == '0.0.1') { + expect(result.link_secret).to.be.a("string"); + expect(result.link_secret).to.eql(Fixtures.Backup.linkSecret.secret); + } + }); - const result = await instance.getAllMessages(); + test("mediators", async () => { + await instance.storeMediator(Fixtures.Backup.mediator); + const result = await instance.backup(version); + if (result.version == '0.0.1') { + expect(result.mediators).to.be.an("array").to.have.length(1); + } + }); - expect(result).to.be.an("array").to.have.length(1); - const msg = result.at(0)!; - expect(msg.ack).to.deep.eq(Fixtures.Backup.message.ack); - expect(msg.attachments).to.deep.eq(Fixtures.Backup.message.attachments); - expect(msg.body).to.deep.eq(Fixtures.Backup.message.body); - expect(msg.createdTime).to.eq(Fixtures.Backup.message.createdTime); - expect(msg.direction).to.eq(Fixtures.Backup.message.direction); - expect(msg.expiresTimePlus).to.eq(Fixtures.Backup.message.expiresTimePlus); - expect(msg.extraHeaders).to.deep.eq(Fixtures.Backup.message.extraHeaders); - expect(msg.from?.toString()).to.eq(Fixtures.Backup.message.from?.toString()); - expect(msg.fromPrior).to.eq(Fixtures.Backup.message.fromPrior); - expect(msg.id).to.eq(Fixtures.Backup.message.id); - expect(msg.piuri).to.eq(Fixtures.Backup.message.piuri); - expect(msg.pthid).to.eq(Fixtures.Backup.message.pthid); - expect(msg.thid).to.eq(Fixtures.Backup.message.thid); - expect(msg.to?.toString()).to.eq(Fixtures.Backup.message.to?.toString()); + test("messages", async () => { + await instance.storeMessage(Fixtures.Backup.message); + const result = await instance.backup(version); + if (result.version == '0.0.1') { + expect(result.messages).to.be.an("array").to.have.length(1); + } + }); + } }); - test("link_secret", async () => { - const secret = "test123"; - await instance.restore({ - credentials: [], - dids: [], - did_pairs: [], - keys: [], - mediators: [], - messages: [], - link_secret: secret, + describe(`Restore ${backupFixture.title}`, () => { + test("credentials - JWT", async () => { + await instance.restore({ + version, + credentials: [ + { + recovery_id: 'jwt', + data: Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Credentials.JWT.credentialPayloadEncoded))).toString(), + }, + ], + dids: [], + did_pairs: [], + keys: [], + mediators: [], + messages: [], + link_secret: undefined, + }); + + const result = await instance.getAllCredentials(); + expect(result).to.be.an("array").to.have.length(1); + const sut = result[0] as JWTCredential; + expect(sut.audience).to.eq(Fixtures.Backup.credentialJWT.audience); + expect(sut.claims).to.deep.eq(Fixtures.Backup.credentialJWT.claims); + expect(sut.context).to.deep.eq(Fixtures.Backup.credentialJWT.context); + expect(sut.credentialSchema).to.deep.eq(Fixtures.Backup.credentialJWT.credentialSchema); + expect(sut.credentialStatus).to.deep.eq(Fixtures.Backup.credentialJWT.credentialStatus); + expect(sut.credentialSubject).to.deep.eq(Fixtures.Backup.credentialJWT.credentialSubject); + expect(sut.credentialType).to.eq(Fixtures.Backup.credentialJWT.credentialType); + expect(sut.evidence).to.deep.eq(Fixtures.Backup.credentialJWT.evidence); + expect(sut.expirationDate).to.eq(Fixtures.Backup.credentialJWT.expirationDate); + expect(sut.id).to.eq(Fixtures.Backup.credentialJWT.id); + expect(sut.issuanceDate).to.eq(Fixtures.Backup.credentialJWT.issuanceDate); + expect(sut.issuer).to.eq(Fixtures.Backup.credentialJWT.issuer); + expect(sut.properties).to.deep.eq(Fixtures.Backup.credentialJWT.properties); + expect(sut.recoveryId).to.eq(Fixtures.Backup.credentialJWT.recoveryId); + expect(sut.refreshService).to.eq(Fixtures.Backup.credentialJWT.refreshService); + expect(sut.subject).to.eq(Fixtures.Backup.credentialJWT.subject); + expect(sut.termsOfUse).to.deep.eq(Fixtures.Backup.credentialJWT.termsOfUse); + expect(sut.type).to.deep.eq(Fixtures.Backup.credentialJWT.type); }); - - const result = await instance.getLinkSecret(); - - expect(result).to.be.instanceOf(Domain.LinkSecret); - expect(result?.secret).to.eq(secret); - }); - - describe("Store not empty - throws", () => { - it("Credentials", async () => { - await instance.storeCredential(Fixtures.Backup.credentialJWT); - expect(instance.restore(Fixtures.Backup.backupJson)).eventually.rejected; + + test("credentials - Anoncreds", async () => { + await instance.restore({ + credentials: [ + { + recovery_id: "anoncred", + data: Buffer.from(base64url.baseEncode(Buffer.from(Fixtures.Backup.credentialAnoncreds.toStorable().credentialData))).toString() + }, + ], + dids: [], + did_pairs: [], + keys: [], + mediators: [], + messages: [], + link_secret: undefined, + }); + + const result = await instance.getAllCredentials(); + expect(result).to.be.an("array").to.have.length(1); + const sut = result[0] as AnonCredsCredential; + expect(sut.claims).to.deep.eq(Fixtures.Backup.credentialAnoncreds.claims); + expect(sut.credentialDefinitionId).to.eq(Fixtures.Backup.credentialAnoncreds.credentialDefinitionId); + expect(sut.credentialType).to.eq(Fixtures.Backup.credentialAnoncreds.credentialType); + expect(sut.id).to.eq(Fixtures.Backup.credentialAnoncreds.id); + expect(sut.issuer).to.eq(Fixtures.Backup.credentialAnoncreds.issuer); + expect(sut.properties).to.deep.eq(Fixtures.Backup.credentialAnoncreds.properties); + expect(sut.recoveryId).to.eq(Fixtures.Backup.credentialAnoncreds.recoveryId); + expect(sut.schemaId).to.eq(Fixtures.Backup.credentialAnoncreds.schemaId); + expect(sut.subject).to.eq(Fixtures.Backup.credentialAnoncreds.subject); }); - - it("DIDs", async () => { - await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); - expect(instance.restore(Fixtures.Backup.backupJson)).eventually.rejected; + + test("dids", async () => { + await instance.restore({ + credentials: [], + dids: [ + { did: Fixtures.Backup.hostDID.toString() }, + ], + did_pairs: [], + keys: [], + mediators: [], + messages: [], + link_secret: undefined, + }); + + const result = await (instance as any).Repositories.DIDs.get(); + + expect(result).to.be.an("array").to.have.length(1); + expect(result[0].toString()).to.eq(Fixtures.Backup.hostDID.toString()); }); - - it("Keys", async () => { - await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); - expect(instance.restore(Fixtures.Backup.backupJson)).eventually.rejected; + + test("did_pairs", async () => { + const name = "test-did-pairs"; + await instance.restore({ + credentials: [], + dids: [], + did_pairs: [ + { + alias: name, + holder: Fixtures.Backup.hostDID.toString(), + recipient: Fixtures.Backup.targetDID.toString(), + } + ], + keys: [], + mediators: [], + messages: [], + link_secret: undefined, + }); + + const result = await instance.getAllDidPairs(); + + expect(result).to.be.an("array").to.have.length(1); + expect(result[0].host.toString()).to.eq(Fixtures.Backup.hostDID.toString()); + expect(result[0].receiver.toString()).to.eq(Fixtures.Backup.targetDID.toString()); + expect(result[0].name).to.eq(name); }); - - it("LinkSecret", async () => { - await instance.storeLinkSecret(Fixtures.Backup.linkSecret); - expect(instance.restore(Fixtures.Backup.backupJson)).eventually.rejected; + + test("keys", async () => { + await instance.restore({ + credentials: [], + dids: [], + did_pairs: [], + keys: [ + { + recovery_id: Fixtures.Backup.secpPrivateKey.recoveryId, + key: Buffer.from(base64url.baseEncode(Buffer.from(JSON.stringify(Fixtures.Backup.secpPrivateKey.to.JWK())))).toString(), + } + ], + mediators: [], + messages: [], + link_secret: undefined, + }); + + const result = await (instance as any).Repositories.Keys.get(); + + expect(result).to.be.an("array").to.have.length(1); + expect(result[0].curve).to.eq(Fixtures.Backup.secpPrivateKey.curve); + expect(result[0].index).to.eq(Fixtures.Backup.secpPrivateKey.index); + expect(result[0].keySpecification).to.deep.eq(Fixtures.Backup.secpPrivateKey.keySpecification); + expect(result[0].raw).to.deep.eq(Fixtures.Backup.secpPrivateKey.raw); + expect(result[0].recoveryId).to.eq(Fixtures.Backup.secpPrivateKey.recoveryId); + expect(result[0].size).to.eq(Fixtures.Backup.secpPrivateKey.size); + expect(result[0].type).to.eq(Fixtures.Backup.secpPrivateKey.type); }); - - it("Messages", async () => { - await instance.storeMessage(Fixtures.Backup.message); - expect(instance.restore(Fixtures.Backup.backupJson)).eventually.rejected; + if (version == "0.0.1") { + test("messages", async () => { + await instance.restore({ + credentials: [], + dids: [], + did_pairs: [], + keys: [], + mediators: [], + messages: [ + "eyJpZCI6IjMwZmIyNTU1LWM5YjgtNDExZC1hOTY2LTg0ZmMwOGJiMWVmZCIsImJvZHkiOnsiY29udGVudCI6IlRlc3QgTWVzc2FnZSJ9LCJwaXVyaSI6Imh0dHBzOi8vZGlkY29tbS5vcmcvYmFzaWNtZXNzYWdlLzIuMC9tZXNzYWdlIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNieXVFOTVXQUpZaFFhcEhSVnVSbTZLTEdiYjc5ZjVVWGlhWVRBWXBVYlpnZC5WejZNa2s0U1l6TER0WkpudVJOMlZKNGtuWmN1SlU0bzVDYjlVVURodkF0YVVaQ3g3LlNleUowSWpvaVpHMGlMQ0p6SWpwN0luVnlhU0k2SW1ScFpEcHdaV1Z5T2pJdVJYbzJURk5uYUhkVFJUUXpOM2R1UkVVeGNIUXpXRFpvVmtSVlVYcFRhbk5JZW1sdWNGZ3pXRVoyVFdwU1FXMDNlUzVXZWpaTmEyaG9NV1UxUTBWWldYRTJTa0pWWTFSYU5rTndNbkpoYmtOWFVuSjJOMWxoZUROTVpUUk9OVGxTTm1Sa0xsTmxlVW93U1dwdmFWcEhNR2xNUTBwNlNXcHdOMGx1Vm5saFUwazJTVzFvTUdSSVFucFBhVGgyWXpKc01FeFlRbmxoV0U1MFRGY3hiRnBIYkdoa1J6bDVURzFHTUZsWGVHaGpTRXB3WXpJd2RXRlhPR2xNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREU1TGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwd04wbHVWbmxoVTBrMlNXNWtlbU42YjNaTU0wNXdaRU14ZDJOdGJIcGlVekYwV2xkU2NGbFlVblpqYVRWb1pFZEdjMWxZUW5saFdFNTBURzFzZGt3elpIcEphWGRwV1ZOSk5sZDVTbXRoVjFKcVlqSXhkRXd6V1hsSmJERTVabEVpTENKeUlqcGJYU3dpWVNJNlcxMTlmUSIsInRvIjoiZGlkOnBlZXI6Mi5FejZMU21Zalk1Y25ISkVEM1J6YkJrb1F1RzVLd3Q5dEM3WHIyNVlCeFRmQ2FoTm9MLlZ6Nk1rdDVCNVR3d2NVaHFTVFZNWFUzV3B6cHhSTXF6WEd5VTkxeUNpZHZ2b1BvTVYuU2V5SnlJanBiWFN3aWN5STZJbVJwWkRwd1pXVnlPakl1UlhvMlRGTm5hSGRUUlRRek4zZHVSRVV4Y0hReldEWm9Wa1JWVVhwVGFuTkllbWx1Y0ZneldFWjJUV3BTUVcwM2VTNVdlalpOYTJob01XVTFRMFZaV1hFMlNrSlZZMVJhTmtOd01uSmhia05YVW5KMk4xbGhlRE5NWlRST05UbFNObVJrTGxObGVVb3dTV3B2YVZwSE1HbE1RMHA2U1dwdmFXRklVakJqU0UwMlRIazVlbUZZVVhSalNFcHdZekl3ZEdKWFZtdGhWMFl3WWpOSmRWbFlVbWhpUjBaM1kyMXNlbUpUTlhCaWVVbHpTVzVKYVU5c2RHUk1RMHBvU1dwd1lrbHRVbkJhUjA1MllsY3dkbVJxU1dsWVdEQWlMQ0poSWpwYlhTd2lkQ0k2SW1SdEluMCIsImNyZWF0ZWRUaW1lIjoxNzA5NjMzOTczNDM4LCJleHBpcmVzVGltZSI6MTcwOTYzMzk3MzQzODg2NDAwLCJhdHRhY2htZW50cyI6W10sImFjayI6W10sImRpcmVjdGlvbiI6MSwiZXh0cmFIZWFkZXJzIjp7fX0" + ], + link_secret: undefined, + }); + + const result = await instance.getAllMessages(); + + expect(result).to.be.an("array").to.have.length(1); + const msg = result.at(0)!; + expect(msg.ack).to.deep.eq(Fixtures.Backup.message.ack); + expect(msg.attachments).to.deep.eq(Fixtures.Backup.message.attachments); + expect(msg.body).to.deep.eq(Fixtures.Backup.message.body); + expect(msg.createdTime).to.eq(Fixtures.Backup.message.createdTime); + expect(msg.direction).to.eq(Fixtures.Backup.message.direction); + expect(msg.expiresTimePlus).to.eq(Fixtures.Backup.message.expiresTimePlus); + expect(msg.extraHeaders).to.deep.eq(Fixtures.Backup.message.extraHeaders); + expect(msg.from?.toString()).to.eq(Fixtures.Backup.message.from?.toString()); + expect(msg.fromPrior).to.eq(Fixtures.Backup.message.fromPrior); + expect(msg.id).to.eq(Fixtures.Backup.message.id); + expect(msg.piuri).to.eq(Fixtures.Backup.message.piuri); + expect(msg.pthid).to.eq(Fixtures.Backup.message.pthid); + expect(msg.thid).to.eq(Fixtures.Backup.message.thid); + expect(msg.to?.toString()).to.eq(Fixtures.Backup.message.to?.toString()); + }); + } + + if (version == "0.0.1") { + test("link_secret", async () => { + const secret = "test123"; + await instance.restore({ + credentials: [], + dids: [], + did_pairs: [], + keys: [], + mediators: [], + messages: [], + link_secret: secret, + }); + + const result = await instance.getLinkSecret(); + + expect(result).to.be.instanceOf(Domain.LinkSecret); + expect(result?.secret).to.eq(secret); + }); + } + + describe("Store not empty - throws", () => { + it("Credentials", async () => { + await instance.storeCredential(Fixtures.Backup.credentialJWT); + expect(instance.restore(backupFixture.json)).eventually.rejected; + }); + + it("DIDs", async () => { + await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); + expect(instance.restore(backupFixture.json)).eventually.rejected; + }); + + it("Keys", async () => { + await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); + expect(instance.restore(backupFixture.json)).eventually.rejected; + }); + + if (version == "0.0.1") { + it("LinkSecret", async () => { + await instance.storeLinkSecret(Fixtures.Backup.linkSecret); + expect(instance.restore(backupFixture.json)).eventually.rejected; + }); + + it("Messages", async () => { + await instance.storeMessage(Fixtures.Backup.message); + expect(instance.restore(backupFixture.json)).eventually.rejected; + }); + } }); }); - }); - - describe("Round trip", () => { - test("Restore -> Backup", async () => { - await instance.restore(Fixtures.Backup.backupJson); - - const backup = await instance.backup(); - - expect(backup.credentials).to.have.length(Fixtures.Backup.backupJson.credentials.length); - expect(backup.credentials).to.have.deep.members(Fixtures.Backup.backupJson.credentials); - - expect(backup.dids).to.have.length(Fixtures.Backup.backupJson.dids.length); - expect(backup.dids).to.have.deep.members(Fixtures.Backup.backupJson.dids); - - expect(backup.did_pairs).to.have.length(Fixtures.Backup.backupJson.did_pairs.length); - expect(backup.did_pairs).to.have.deep.members(Fixtures.Backup.backupJson.did_pairs); - - expect(backup.keys).to.have.length(Fixtures.Backup.backupJson.keys.length); - expect(backup.keys).to.have.deep.members(Fixtures.Backup.backupJson.keys); - - expect(backup.messages).to.have.length(Fixtures.Backup.backupJson.messages.length); - expect(backup.messages).to.have.deep.members(Fixtures.Backup.backupJson.messages); - - expect(backup.link_secret).to.eq(Fixtures.Backup.backupJson.link_secret); + + describe(`Round trip ${backupFixture.title}`, () => { + test("Restore -> Backup", async () => { + await instance.restore(backupFixture.json); + + const backup = await instance.backup(version); + + expect(backup.credentials).to.have.length(backupFixture.json.credentials.length); + expect(backup.credentials).to.have.deep.members(backupFixture.json.credentials); + + expect(backup.dids).to.have.length(backupFixture.json.dids.length); + expect(backup.dids).to.have.deep.members(backupFixture.json.dids); + + expect(backup.did_pairs).to.have.length(backupFixture.json.did_pairs.length); + expect(backup.did_pairs).to.have.deep.members(backupFixture.json.did_pairs); + + expect(backup.keys).to.have.length(backupFixture.json.keys.length); + expect(backup.keys).to.have.deep.members(backupFixture.json.keys); + + if (backup.version == "0.0.1" && backupFixture.json.version == "0.0.1") { + expect(backup.messages).to.have.length(backupFixture.json.messages.length); + expect(backup.messages).to.have.deep.members(backupFixture.json.messages); + expect(backup.link_secret).to.eq(backupFixture.json.link_secret); + } + + }); + + test("Backup -> Restore", async () => { + await instance.storeCredential(Fixtures.Backup.credentialJWT); + await instance.storeCredential(Fixtures.Backup.credentialAnoncreds); + await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); + await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); + if (backupFixture.json.version == "0.0.1") { + await instance.storeLinkSecret(Fixtures.Backup.linkSecret); + await instance.storeMessage(Fixtures.Backup.message); + await instance.storeMediator(Fixtures.Backup.mediator); + } + + const backup = await instance.backup(version); + + expect(backup).not.to.be.null; + + const sut = mockPluto(); + await sut.start(); + await sut.restore(backup); + + const credentials = await sut.getAllCredentials(); + const dids = await sut.getAllPeerDIDs(); + const didPairs = await sut.getAllDidPairs(); + const keys = await sut.getDIDPrivateKeysByDID(Fixtures.Backup.hostDID); + const mediators = await sut.getAllMediators(); + const messages = await sut.getAllMessages(); + const linksecret = await sut.getLinkSecret(); + + expect(dids).not.to.be.null; + + expect(credentials).to.have.length(2); + // expect(credentials.map(x => (x as any).toStorable())).to.have.deep.members([Fixtures.Backup.credentialAnoncreds.toStorable(), Fixtures.Backup.credentialJWT.toStorable()]); + + expect(dids).to.have.length(2); + expect(didPairs).to.have.length(1); + expect(keys).to.have.length(1); + if (version == "0.0.1") { + expect(mediators).to.have.length(1); + expect(messages).to.have.length(1); + } + + }); }); - test("Backup -> Restore", async () => { - await instance.storeCredential(Fixtures.Backup.credentialJWT); - await instance.storeCredential(Fixtures.Backup.credentialAnoncreds); - await instance.storeDIDPair(Fixtures.Backup.hostDID, Fixtures.Backup.targetDID, Fixtures.Backup.pairAlias); - await instance.storeDID(Fixtures.Backup.hostDID, Fixtures.Backup.peerDIDKeys); - await instance.storeLinkSecret(Fixtures.Backup.linkSecret); - await instance.storeMessage(Fixtures.Backup.message); - await instance.storeMediator(Fixtures.Backup.mediator); - - const backup = await instance.backup(); - - expect(backup).not.to.be.null; - - const sut = mockPluto(); - await sut.start(); - await sut.restore(backup); - - const credentials = await sut.getAllCredentials(); - const dids = await sut.getAllPeerDIDs(); - const didPairs = await sut.getAllDidPairs(); - const keys = await sut.getDIDPrivateKeysByDID(Fixtures.Backup.hostDID); - const mediators = await sut.getAllMediators(); - const messages = await sut.getAllMessages(); - const linksecret = await sut.getLinkSecret(); - - expect(dids).not.to.be.null; - - expect(credentials).to.have.length(2); - // expect(credentials.map(x => (x as any).toStorable())).to.have.deep.members([Fixtures.Backup.credentialAnoncreds.toStorable(), Fixtures.Backup.credentialJWT.toStorable()]); - - expect(dids).to.have.length(2); - expect(didPairs).to.have.length(1); - expect(keys).to.have.length(1); - expect(mediators).to.have.length(1); - expect(messages).to.have.length(1); - - }); }); + });