diff --git a/CHANGELOG.md b/CHANGELOG.md index 9921665..9c6b16f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Build-time environment variable `CONCORDIUM_WALLET_CRYPTO_FRAMEWORK_PATH` for resolving the framework to a path instead of using the released one. +- Functions and associated types for creating identity issuance and recovery requests as well as account credential (deployment). + + UDL signatures: + + - `string identity_issuance_request_json(IdentityIssuanceRequestParameters params)` + - `string identity_recovery_request_json(IdentityRecoveryRequestParameters params)` + - `AccountCredentialResult account_credential(AccountCredentialParameters params)` + - `string account_credential_deployment_hash_hex(AccountCredential credential, u64 expiry_unix)` + - `string account_credential_deployment_signed_payload_hex(SignedAccountCredential credential)` + + The parameter types mirror the corresponding "input" types from `wallet_library` but use only types supported by UniFFI. + The values are translated into these library types via JSON encoding/decoding. + This is in contrast to the Java SDK where the value is passed as a JSON encoded string + which is then decoded directly into the library input type. + Doing it this way ensures that the conversions that are only checked at runtime happen internally in this library is easily tested, + rather than across the FFI boundary. + So it makes the FFI boundary statically typed and of course also generates the Swift types that we do need on the SDK side anyway. + + The identity request functions return their result as JSON encoded strings + because the protocol actually is to just send the object as JSON in a URL parameter. + So there's no point in decoding them into structured types - they would just be converted right back to JSON on the SDK side. + We do decode the payload in a unit test to verify the format. + ### Changed - Rename crate from "crypto" to "concordium-wallet-crypto-uniffi" and verify on CI that project works on all platforms. - Rename generated framework from "RustFramework" to "ConcordiumWalletCryptoUniffi". - Ensure that library builds on macOS 11+ and verify on CI. - Bump UniFFI from v0.25.x to v0.26.x. +- Rename functions to match the usage and conventions on the SDK side. ## [1.0.0] - 2024-02-06 diff --git a/Cargo.lock b/Cargo.lock index 247d0aa..7e8794e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -546,6 +546,9 @@ dependencies = [ name = "concordium-wallet-crypto-uniffi" version = "1.0.0" dependencies = [ + "concordium_base", + "serde", + "serde_json", "thiserror", "uniffi", "wallet_library", diff --git a/Cargo.toml b/Cargo.toml index 7631f70..c359851 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,14 @@ crate-type = ["staticlib", "lib"] [dependencies] thiserror = "1" uniffi = { version = "0.26", features = ["cli"] } -wallet_library = { path = "./concordium-base/rust-src/wallet_library" } +serde = "1.0" +serde_json = "1.0" + +[dependencies.concordium_base] +path = "./concordium-base/rust-src/concordium_base" + +[dependencies.wallet_library] +path = "./concordium-base/rust-src/wallet_library" [build-dependencies] uniffi = { version = "0.26", features = ["build"] } diff --git a/src/lib.rs b/src/lib.rs index cb6d636..ca462ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,23 @@ -use wallet_library::wallet::{ - get_account_public_key_aux, get_account_signing_key_aux, - get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, - get_prf_key_aux, get_signature_blinding_randomness_aux, - get_verifiable_credential_backup_encryption_key_aux, get_verifiable_credential_public_key_aux, - get_verifiable_credential_signing_key_aux, +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use uniffi::deps::anyhow::Context; +use wallet_library::{ + credential::{ + compute_credential_deployment_hash_to_sign, create_unsigned_credential_v1_aux, + serialize_credential_deployment_payload, CredentialDeploymentDetails, + CredentialDeploymentPayload, UnsignedCredentialInput, + }, + identity::{ + create_identity_object_request_v1_aux, create_identity_recovery_request_aux, + IdentityObjectRequestInput, IdentityRecoveryRequestInput, + }, + wallet::{ + get_account_public_key_aux, get_account_signing_key_aux, + get_attribute_commitment_randomness_aux, get_credential_id_aux, get_id_cred_sec_aux, + get_prf_key_aux, get_signature_blinding_randomness_aux, + get_verifiable_credential_backup_encryption_key_aux, + get_verifiable_credential_public_key_aux, get_verifiable_credential_signing_key_aux, + }, }; // UniFFI book: https://mozilla.github.io/uniffi-rs/udl_file_spec.html @@ -17,104 +31,112 @@ pub enum ConcordiumWalletCryptoError { CallFailed { call: String, msg: String }, } -pub fn get_account_signing_key( +/// Implements UDL definition of the same name. +pub fn identity_cred_sec_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, - credential_counter: u32, ) -> Result { - get_account_signing_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter) + get_id_cred_sec_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_account_signing_key(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter})"), - msg: e.to_string(), + call: format!("identity_cred_sec_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), + msg: format!("{:#}", e), }) } -pub fn get_account_public_key( +/// Implements UDL definition of the same name. +pub fn identity_prf_key_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, - credential_counter: u32, ) -> Result { - get_account_public_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter) + get_prf_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_account_public_key(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter})"), - msg: e.to_string(), + call: format!("identity_prf_key_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), + msg: format!("{:#}", e), }) } -pub fn get_id_cred_sec( +/// Implements UDL definition of the same name. +pub fn identity_attributes_signature_blinding_randomness_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, ) -> Result { - get_id_cred_sec_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) + get_signature_blinding_randomness_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_id_cred_sec(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), - msg: e.to_string(), + call: format!("identity_attributes_signature_blinding_randomness_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), + msg: format!("{:#}", e), }) } -pub fn get_prf_key( +/// Implements UDL definition of the same name. +pub fn account_credential_signing_key_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, + credential_counter: u8, ) -> Result { - get_prf_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) + get_account_signing_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter.into()) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_prf_key(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), - msg: e.to_string(), + call: format!("account_credential_signing_key_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter})"), + msg: format!("{:#}", e), }) } -pub fn get_credential_id( +/// Implements UDL definition of the same name. +pub fn account_credential_public_key_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, credential_counter: u8, - commitment_key: String, ) -> Result { - get_credential_id_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter, commitment_key.as_str()) + get_account_public_key_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter.into()) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_credential_id(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter}, commitment_key={commitment_key})"), - msg: e.to_string(), + call: format!("account_credential_public_key_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter})"), + msg: format!("{:#}", e), }) } -pub fn get_signature_blinding_randomness( +/// Implements UDL definition of the same name. +pub fn account_credential_id_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, + credential_counter: u8, + commitment_key: String, ) -> Result { - get_signature_blinding_randomness_aux(seed_hex, net.as_str(), identity_provider_index, identity_index) + get_credential_id_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter, commitment_key.as_str()) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_signature_blinding_randomness(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index})"), - msg: e.to_string(), + call: format!("account_credential_id_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter}, commitment_key={commitment_key})"), + msg: format!("{:#}", e), }) } -pub fn get_attribute_commitment_randomness( +/// Implements UDL definition of the same name. +pub fn account_credential_attribute_commitment_randomness_hex( seed_hex: String, net: String, identity_provider_index: u32, identity_index: u32, - credential_counter: u32, + credential_counter: u8, attribute: u8, ) -> Result { - get_attribute_commitment_randomness_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter, attribute) + get_attribute_commitment_randomness_aux(seed_hex, net.as_str(), identity_provider_index, identity_index, credential_counter.into(), attribute) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_attribute_commitment_randomness(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter}, attribute={attribute})"), - msg: e.to_string(), + call: format!("account_credential_attribute_commitment_randomness_hex(seed_hex, net={net}, identity_provider_index={identity_provider_index}, identity_index={identity_index}, credential_counter={credential_counter}, attribute={attribute})"), + msg: format!("{:#}", e), }) } -pub fn get_verifiable_credential_signing_key( +/// Implements UDL definition of the same name. +pub fn verifiable_credential_signing_key_hex( seed_hex: String, net: String, issuer_index: u64, @@ -123,12 +145,13 @@ pub fn get_verifiable_credential_signing_key( ) -> Result { get_verifiable_credential_signing_key_aux(seed_hex, net.as_str(), issuer_index, issuer_subindex, verifiable_credential_index) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_verifiable_credential_signing_key(seed_hex, net={net}, issuer_index={issuer_index}, issuer_subindex={issuer_subindex}, verifiable_credential_index={verifiable_credential_index})"), - msg: e.to_string(), + call: format!("verifiable_credential_signing_key_hex(seed_hex, net={net}, issuer_index={issuer_index}, issuer_subindex={issuer_subindex}, verifiable_credential_index={verifiable_credential_index})"), + msg: format!("{:#}", e), }) } -pub fn get_verifiable_credential_public_key( +/// Implements UDL definition of the same name. +pub fn verifiable_credential_public_key_hex( seed_hex: String, net: String, issuer_index: u64, @@ -137,19 +160,449 @@ pub fn get_verifiable_credential_public_key( ) -> Result { get_verifiable_credential_public_key_aux(seed_hex, net.as_str(), issuer_index, issuer_subindex, verifiable_credential_index) .map_err(|e| ConcordiumWalletCryptoError::CallFailed { - call: format!("get_verifiable_credential_public_key(seed_hex, net={net}, issuer_index={issuer_index}, issuer_subindex={issuer_subindex}, verifiable_credential_index={verifiable_credential_index})"), - msg: e.to_string(), + call: format!("verifiable_credential_public_key_hex(seed_hex, net={net}, issuer_index={issuer_index}, issuer_subindex={issuer_subindex}, verifiable_credential_index={verifiable_credential_index})"), + msg: format!("{:#}", e), }) } -pub fn get_verifiable_credential_backup_encryption_key( +/// Implements UDL definition of the same name. +pub fn verifiable_credential_backup_encryption_key_hex( seed_hex: String, net: String, ) -> Result { get_verifiable_credential_backup_encryption_key_aux(seed_hex, net.as_str()).map_err(|e| { ConcordiumWalletCryptoError::CallFailed { - call: format!("get_verifiable_credential_backup_encryption_key(seed_hex, net={net}"), - msg: e.to_string(), + call: format!("verifiable_credential_backup_encryption_key_hex(seed_hex, net={net}"), + msg: format!("{:#}", e), } }) } + +/// UniFFI compatible bridge to [`IdentityObjectRequestInput`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct IdentityIssuanceRequestParameters { + #[serde(rename = "ipInfo")] + pub ip_info: IdentityProviderInfo, + #[serde(rename = "globalContext")] + pub global_context: GlobalContext, + #[serde(rename = "arsInfos")] + pub ars_infos: HashMap, + #[serde(rename = "arThreshold")] + pub ar_threshold: u8, + #[serde(rename = "prfKey")] + pub prf_key_hex: String, + #[serde(rename = "idCredSec")] + pub id_cred_sec_hex: String, + #[serde(rename = "blindingRandomness")] + pub blinding_randomness_hex: String, +} + +/// UniFFI compatible bridge to [`IdentityRecoveryRequestInput`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct IdentityRecoveryRequestParameters { + #[serde(rename = "ipInfo")] + pub ip_info: IdentityProviderInfo, + #[serde(rename = "globalContext")] + pub global_context: GlobalContext, + #[serde(rename = "timestamp")] + pub timestamp: u64, + #[serde(rename = "idCredSec")] + pub id_cred_sec_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::IpInfo`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct IdentityProviderInfo { + #[serde(rename = "ipIdentity")] + pub identity: u32, + #[serde(rename = "ipDescription")] + pub description: Description, + #[serde(rename = "ipVerifyKey")] + pub verify_key_hex: String, + #[serde(rename = "ipCdiVerifyKey")] + pub cdi_verify_key_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::GlobalContext`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct GlobalContext { + #[serde(rename = "onChainCommitmentKey")] + pub on_chain_commitment_key_hex: String, + #[serde(rename = "bulletproofGenerators")] + pub bulletproof_generators_hex: String, + #[serde(rename = "genesisString")] + pub genesis_string: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::ArInfo`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct AnonymityRevokerInfo { + #[serde(rename = "arIdentity")] + pub identity: u32, + #[serde(rename = "arDescription")] + pub description: Description, + #[serde(rename = "arPublicKey")] + pub public_key_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::Description`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Clone, Debug, Serialize)] +pub struct Description { + #[serde(rename = "name")] + pub name: String, + #[serde(rename = "url")] + pub url: String, + #[serde(rename = "description")] + pub description: String, +} + +/// UniFFI compatible bridge to [`UnsignedCredentialInput`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Serialize)] +pub struct AccountCredentialParameters { + #[serde(rename = "ipInfo")] + pub ip_info: IdentityProviderInfo, + #[serde(rename = "globalContext")] + pub global_context: GlobalContext, + #[serde(rename = "arsInfos")] + pub ars_infos: HashMap, + #[serde(rename = "idObject")] + pub id_object: IdentityObject, + #[serde(rename = "revealedAttributes")] + pub revealed_attributes: Vec, + #[serde(rename = "credNumber")] + pub cred_number: u8, + #[serde(rename = "idCredSec")] + pub id_cred_sec_hex: String, + #[serde(rename = "prfKey")] + pub prf_key_hex: String, + #[serde(rename = "blindingRandomness")] + pub blinding_randomness_hex: String, + #[serde(rename = "attributeRandomness")] + pub attribute_randomness_hex: HashMap, + #[serde(rename = "credentialPublicKeys")] + pub credential_public_keys: CredentialPublicKeys, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::IdentityObjectV1`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Serialize)] +pub struct IdentityObject { + #[serde(rename = "preIdentityObject")] + pub pre_identity_object: PreIdentityObject, + #[serde(rename = "attributeList")] + pub attribute_list: AttributeList, + #[serde(rename = "signature")] + pub signature_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::PreIdentityObjectV1`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct PreIdentityObject { + #[serde(rename = "idCredPub")] + pub id_cred_pub_hex: String, + #[serde(rename = "ipArData")] + pub ip_ar_data: HashMap, + #[serde(rename = "choiceArData")] + pub choice_ar_data: ChoiceArParameters, + #[serde(rename = "idCredSecCommitment")] + pub id_cred_sec_commitment_hex: String, + #[serde(rename = "prfKeyCommitmentWithIP")] + pub prf_key_commitment_with_ip_hex: String, + #[serde(rename = "prfKeySharingCoeffCommitments")] + pub prf_key_sharing_coeff_commitments_hex: Vec, + #[serde(rename = "proofsOfKnowledge")] + pub proofs_of_knowledge_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::ChoiceArParameters`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct ChoiceArParameters { + #[serde(rename = "arIdentities")] + pub ar_identities: Vec, + #[serde(rename = "threshold")] + pub threshold: u32, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::IpArData`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct ArData { + #[serde(rename = "encPrfKeyShare")] + pub enc_prf_key_share_hex: String, + #[serde(rename = "proofComEncEq")] + pub proof_com_enc_eq_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::AttributeList `], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Serialize)] +pub struct AttributeList { + #[serde(rename = "validTo")] + pub valid_to_year_month: String, + #[serde(rename = "createdAt")] + pub created_at_year_month: String, + #[serde(rename = "maxAccounts")] + pub max_accounts: u8, + #[serde(rename = "chosenAttributes")] + pub chosen_attributes: HashMap, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::CredentialPublicKeys`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize)] +pub struct CredentialPublicKeys { + #[serde(rename = "keys")] + pub keys: HashMap, + #[serde(rename = "threshold")] + pub threshold: u8, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::VerifyKey`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize)] +pub struct VerifyKey { + #[serde(rename = "schemeId")] + pub scheme_id: String, + #[serde(rename = "verifyKey")] + pub key_hex: String, +} + +/* OUTPUTS */ + +/// UniFFI compatible bridge to [`wallet_library::credential::UnsignedCredentialDeploymentInfoWithRandomness`] (internal), +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize)] +pub struct AccountCredentialResult { + #[serde(rename = "unsignedCdi")] + credential: AccountCredential, + #[serde(rename = "randomness")] + randomness: Randomness, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::CommitmentsRandomness`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize)] +pub struct Randomness { + #[serde(rename = "attributesRand")] + pub attributes_rand_hex: HashMap, + #[serde(rename = "credCounterRand")] + pub cred_counter_rand_hex: String, + #[serde(rename = "idCredSecRand")] + pub id_cred_sec_rand_hex: String, + #[serde(rename = "maxAccountsRand")] + pub max_accounts_rand_hex: String, + #[serde(rename = "prfRand")] + pub prf_rand_hex: String, +} + +/// Implements UDL definition of the same name. +/// The returned string is a versioned [`PreIdentityObject`] encoded as JSON. +pub fn identity_issuance_request_json( + params: IdentityIssuanceRequestParameters, +) -> Result { + serde_json::to_string(¶ms) + .context("cannot encode request object as JSON") + .and_then(|json| { + serde_json::from_str::(&json) + .context("cannot decode request object into internal type") + }) + .and_then(|input| { + create_identity_object_request_v1_aux(input).context("cannot create identity") + }) + .map_err(|e| ConcordiumWalletCryptoError::CallFailed { + call: "identity_issuance_request_json(...)".to_string(), + msg: format!("{:#}", e), + }) +} + +/// Implements UDL definition of the same name. +/// The returned string is a versioned `IdentityRecoveryRequestResultValue` (defined in `lib_test.rs`) encoded as JSON. +pub fn identity_recovery_request_json( + params: IdentityRecoveryRequestParameters, +) -> Result { + serde_json::to_string(¶ms) + .context("cannot encode request object as JSON") + .and_then(|json| { + serde_json::from_str::(&json) + .context("cannot decode request object into internal type") + }) + .and_then(|input| { + create_identity_recovery_request_aux(input).context("cannot create identity") + }) + .map_err(|e| ConcordiumWalletCryptoError::CallFailed { + call: "identity_recovery_request_json(...)".to_string(), + msg: format!("{:#}", e), + }) +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::UnsignedCredentialDeploymentInfo`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize)] +pub struct AccountCredential { + #[serde(rename = "arData")] + pub ar_data: HashMap, + #[serde(rename = "credId")] + pub cred_id_hex: String, + #[serde(rename = "credentialPublicKeys")] + pub credential_public_keys: CredentialPublicKeys, + #[serde(rename = "ipIdentity")] + pub ip_identity: u32, + #[serde(rename = "policy")] + pub policy: Policy, + #[serde(rename = "proofs")] + pub proofs: Proofs, + #[serde(rename = "revocationThreshold")] + pub revocation_threshold: u8, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::ChainArData`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize)] +pub struct ChainArData { + #[serde(rename = "encIdCredPubShare")] + pub end_id_cred_pub_share_hex: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::Policy `], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize)] +pub struct Policy { + #[serde(rename = "createdAt")] + pub created_at_year_month: String, + #[serde(rename = "revealedAttributes")] + pub revealed_attributes: HashMap, + #[serde(rename = "validTo")] + pub valid_to_year_month: String, +} + +/// UniFFI compatible bridge to [`concordium_base::id::types::IdOwnershipProofs`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Deserialize, Serialize, PartialEq)] +pub struct Proofs { + #[serde(rename = "challenge")] + pub challenge_hex: String, + #[serde(rename = "commitments")] + pub commitments_hex: String, + #[serde(rename = "credCounterLessThanMaxAccounts")] + pub cred_counter_less_than_max_accounts_hex: String, + #[serde(rename = "proofIdCredPub")] + pub proof_id_cred_pub_hex: HashMap, + #[serde(rename = "proofIpSig")] + pub proof_ip_sig_hex: String, + #[serde(rename = "proofRegId")] + pub proof_reg_id_hex: String, + #[serde(rename = "sig")] + pub signature_hex: String, +} + +/// Implements UDL definition of the same name. +pub fn account_credential( + params: AccountCredentialParameters, +) -> Result { + serde_json::to_string(¶ms) + .context("cannot encode request object as JSON") + .and_then(|json| { + serde_json::from_str::(&json) + .context("cannot decode request object into internal type") + }) + .and_then(|input| { + create_unsigned_credential_v1_aux(input).context("cannot create identity") + }) + .and_then(|res| { + serde_json::from_str::(&res) + .context("cannot decode response object into result type") + }) + .map_err(|e| ConcordiumWalletCryptoError::CallFailed { + call: "account_credential(...)".to_string(), + msg: format!("{:#}", e), + }) +} + +/// Duplicate of [`CredentialDeploymentDetails`] because that type only supports construction from JSON. +/// It's only used internally and not exported. +#[derive(Debug, Serialize)] +struct CredentialDeploymentPayloadHashInput { + #[serde(rename = "expiry")] + expiry_unix_secs: u64, + #[serde(rename = "unsignedCdi")] + credential: AccountCredential, +} + +/// Implements UDL definition of the same name. +pub fn account_credential_deployment_hash_hex( + credential: AccountCredential, + expiry_unix_secs: u64, +) -> Result { + let input = CredentialDeploymentPayloadHashInput { + expiry_unix_secs, + credential, + }; + serde_json::to_string(&input) + .context("cannot encode request object as JSON") + .and_then(|json| { + serde_json::from_str::(&json) + .context("cannot decode request object into internal type") + }) + .map(compute_credential_deployment_hash_to_sign) + .map_err(|e| ConcordiumWalletCryptoError::CallFailed { + call: "account_credential_deployment_hash_hex(...)".to_string(), + msg: format!("{:#}", e), + }) +} + +/// UniFFI compatible bridge to [`CredentialDeploymentPayload`], +/// providing the implementation of the UDL declaration of the same name. +/// The translation is performed using Serde. +#[derive(Debug, Serialize)] +pub struct SignedAccountCredential { + #[serde(rename = "unsignedCdi")] + pub credential: AccountCredential, + #[serde(rename = "signatures")] + pub signatures_hex: HashMap, +} + +/// Implements UDL definition of the same name. +pub fn account_credential_deployment_signed_payload_hex( + credential: SignedAccountCredential, +) -> Result { + serde_json::to_string(&credential) + .context("cannot encode request object as JSON") + .and_then(|json| { + serde_json::from_str::(&json) + .context("cannot decode request object into internal type") + }) + .map(serialize_credential_deployment_payload) + .map_err(|e| ConcordiumWalletCryptoError::CallFailed { + call: "account_credential_deployment_signed_payload_hex(...)".to_string(), + msg: format!("{:#}", e), + }) +} diff --git a/src/lib.udl b/src/lib.udl index 26a5cea..2cb920e 100644 --- a/src/lib.udl +++ b/src/lib.udl @@ -3,25 +3,331 @@ enum ConcordiumWalletCryptoError { "CallFailed" }; +/// Parameter object for `identity_issuance_request_json`. +dictionary IdentityIssuanceRequestParameters { + IdentityProviderInfo ip_info; + GlobalContext global_context; + record ars_infos; + u8 ar_threshold; + string prf_key_hex; + string id_cred_sec_hex; + string blinding_randomness_hex; +}; + +/// Parameter object for `identity_recovery_request_json`. +dictionary IdentityRecoveryRequestParameters { + IdentityProviderInfo ip_info; + GlobalContext global_context; + u64 timestamp; + string id_cred_sec_hex; +}; + +/// Public information about an identity provider. +dictionary IdentityProviderInfo { + /// Unique identifier of the identity provider. + u32 identity; + /// Free form description, e.g., how to contact them off-chain. + Description description; + /// PS public key of the identity provider. + string verify_key_hex; + /// Ed public key of the identity provider. + string cdi_verify_key_hex; +}; + +/// A set of cryptographic parameters that are particular to the chain and +/// shared by everybody that interacts with the chain. +dictionary GlobalContext { + /// Shared commitment key known to the chain and the account holder (i.e. it's public). + /// The account holder uses this commitment key to generate commitments to values in the attribute list. + string on_chain_commitment_key_hex; + /// Generators for the bulletproofs. + string bulletproof_generators_hex; + /// Free-form string used to distinguish between different chains even if they share other parameters. + string genesis_string; +}; + +/// Information on an anonymity revoker held by the identity provider. +dictionary AnonymityRevokerInfo { + /// Unique identifier of the anonymity revoker. + u32 identity; + /// Description of the anonymity revoker (e.g. name, contact number). + Description description; + /// Elgamal encryption key of the anonymity revoker. + string public_key_hex; +}; + +/// Metadata of an identity provider or anonymity revoker +/// to be visible on the chain. +dictionary Description { + string name; + string url; + string description; +}; + +/// Parameter object for `account_credential`. +dictionary AccountCredentialParameters { + IdentityProviderInfo ip_info; + GlobalContext global_context; + record ars_infos; + IdentityObject id_object; + sequence revealed_attributes; + u8 cred_number; + string id_cred_sec_hex; + string prf_key_hex; + string blinding_randomness_hex; + record attribute_randomness_hex; + CredentialPublicKeys credential_public_keys; +}; + +/// The data we get back from the identity provider in the version 1 flow. +dictionary IdentityObject { + PreIdentityObject pre_identity_object; + /// Chosen attribute list. + AttributeList attribute_list; + string signature_hex; +}; + +/// Information sent from the account holder to the identity provider. +/// This includes only the cryptographic parts; the attribute list is in a different object below. +/// This is for the flow where no initial account is involved. +dictionary PreIdentityObject { + string id_cred_pub_hex; + /// Anonymity revocation data for the chosen anonymity revokers. + record ip_ar_data; + /// Choice of anonyimity revocation parameters. + /// Identity provider checks that the values make sense in the context of the public keys they are allowed to use. + ChoiceArParameters choice_ar_data; + /// Commitment to ID cred sec using the commitment key of the identity provider derived from the PS public key. + /// This is used to compute the message that the identity provider signs. + string id_cred_sec_commitment_hex; + /// Commitment to the PRF key in group G1. + string prf_key_commitment_with_ip_hex; + /// Commitments to the coefficients of the polynomial used to share the PRF key. + sequence prf_key_sharing_coeff_commitments_hex; + /// Proof that the data sent to the identity provider is well-formed. + string proofs_of_knowledge_hex; +}; + +/// Choice of anonymity revocation parameters. +dictionary ChoiceArParameters { + sequence ar_identities; + u32 threshold; +}; + +/// The data relating to a single anonymity revoker +/// sent by the account holder to the identity provider. +/// Typically the account holder will send a vector of these. +dictionary ArData { + /// Encryption in chunks (in little endian) of the PRF key share. + string enc_prf_key_share_hex; + /// Response in the proof that the computed commitment to the share + /// contains the same value as the encryption. + /// The commitment to the share is not sent but computed from the commitments to the sharing coefficients. + string proof_com_enc_eq_hex; +}; + +/// An attribute list that is part of a normal credential. +/// It consists of some mandatory attributes and some user selected attributes. +dictionary AttributeList { + /// The latest month and year where the credential is still valid. + string valid_to_year_month; + /// The year and month when the identity object from which the credential is derived was created. + /// This deliberately has low granularity since if it was, e.g., a unix timestamp in seconds, + /// then the identity provider could link accounts on the chain to identities they have issued. + string created_at_year_month; + /// Maximum number of accounts that can be created from the owning identity object. + u8 max_accounts; + /// The attributes map. + record chosen_attributes; +}; + +/// The result of a new credential being created using the function `account_credential`. +dictionary AccountCredentialResult { + AccountCredential credential; + Randomness randomness; +}; + +/// Randomness that is generated to commit to attributes when creating a credential. +/// This randomness is needed to do something with those commitments later, +/// for example reveal the committed value or prove a property of the value. +dictionary Randomness { + /// Randomness used to commit to any user-chosen attributes, such as country of nationality. + record attributes_rand_hex; + /// Randomness of the commitment to the credential nonce. + /// This nonce is the number that is used to ensure that only a limited number of credentials + /// can be created from a given identity object. + string cred_counter_rand_hex; + /// Randomness of the commitment to idCredSec. + string id_cred_sec_rand_hex; + /// Randomness of the commitment to the maximum number of accounts that may be created from the identity object. + string max_accounts_rand_hex; + /// Randomness of the commitment to the PRF key. + string prf_rand_hex; +}; + +/// An account credential containing proofs without signatures. +/// To deploy a credential, an object of this type is +/// hashed using `account_credential_deployment_hash_hex` +/// which is signed using the key derived for the credential. +/// The results are collected into a `SignedAccountCredential` +/// and serialized using `account_credential_deployment_signed_payload_hex`. +/// The result of this call can be submitted to the chain. +dictionary AccountCredential { + /// List of anonymity revokers which can revoke the identity. + /// The order is important since it is the same order as that signed by the identity provider, + /// and permuting the list will invalidate the signature from the identity provider. + record ar_data; + /// Credential registration ID of the credential. + string cred_id_hex; + /// Credential keys (i.e. account holder keys). + CredentialPublicKeys credential_public_keys; + /// Identity of the identity provider who signed the identity object + /// from which this credential is derived. + u32 ip_identity; + /// Policy of this credential object. + Policy policy; + Proofs proofs; + /// Anonymity revocation threshold. Must be less than the number of entries in `ar_data`. + u8 revocation_threshold; +}; + +/// Data relating to a single anonymity revoker constructed by the account holder. +/// Typically a vector of these will be sent to the chain. +dictionary ChainArData { + string end_id_cred_pub_share_hex; +}; + +/// Public credential keys currently on the account. +/// The threshold determines the number of required signatures on a transaction for it to be valid. +dictionary CredentialPublicKeys { + record keys; + u8 threshold; +}; + +/// Public AKA verification key for a given scheme. +/// Currently the only supported value of `scheme_id` is "Ed25519". +dictionary VerifyKey { + string scheme_id; + string key_hex; +}; + +/// A policy is (currently) revealed values of attributes that are part of the identity object. +/// Policies are part of credentials. +dictionary Policy { + string created_at_year_month; + string valid_to_year_month; + record revealed_attributes; +}; + +/// All proofs required to prove ownership of an identity in a credential deployment. +dictionary Proofs { + /// Challenge used for all of the proofs. + string challenge_hex; + /// List of commitments to the attributes. + string commitments_hex; + /// Proof that credential counter is at most equal to the maximum allowed number of account. + string cred_counter_less_than_max_accounts_hex; + /// Responses in the proof that the computed commitment to the share + /// contains the same value as the encryption. + /// The commitment to the share is not sent but computed from the commitments to the sharing coefficients. + record proof_id_cred_pub_hex; + /// Responses in the proof of knowledge of signature of the identity provider. + string proof_ip_sig_hex; + /// Proof that registration ID is valid and computed from the PRF key signed by the identity provider. + string proof_reg_id_hex; + /// (Blinded) signature derived from the signature on the pre-identity object by the identity provider. + string signature_hex; +}; + +/// The credential deployment context required to serialize a credential deployment for submission to the chain. +dictionary SignedAccountCredential { + AccountCredential credential; + record signatures_hex; +}; + namespace crypto { + /// Compute the IdCredSec for the provided seed and identity indexes. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. + [Throws=ConcordiumWalletCryptoError] + string identity_cred_sec_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + + /// Compute the PRF-key for the provided seed and identity indexes. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. + [Throws=ConcordiumWalletCryptoError] + string identity_prf_key_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + + /// Compute the signature blinding randomness for the provided seed and identity indexes. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_account_signing_key(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u32 credential_counter); + string identity_attributes_signature_blinding_randomness_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + + /// Compute the account credential signing key for the provided seed, identity indexes, and credential counter. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_account_public_key(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u32 credential_counter); + string account_credential_signing_key_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u8 credential_counter); + + /// Compute the account credential public key for the provided seed, identity indexes, and credential counter. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_id_cred_sec(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + string account_credential_public_key_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u8 credential_counter); + + /// Compute the credential ID for the provided seed, identity indexes, credential counter, and the chain's commitment key. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_prf_key(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + string account_credential_id_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u8 credential_counter, string commitment_key); + + /// Compute the attribute commitment randomness for the provided seed, identity indexes, credential counter, and attribute number. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_credential_id(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u8 credential_counter, string commitment_key); + string account_credential_attribute_commitment_randomness_hex(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u8 credential_counter, u8 attribute); + + /// Compute the signing key for the provided seed, issuer indexes, and verifiable credential index. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_signature_blinding_randomness(string seed_hex, string network, u32 identity_provider_index, u32 identity_index); + string verifiable_credential_signing_key_hex(string seed_hex, string network, u64 issuer_index, u64 issuer_subindex, u32 verifiable_credential_index); + + /// Compute the public key for the provided seed, issuer indexes, and verifiable credential index. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_attribute_commitment_randomness(string seed_hex, string network, u32 identity_provider_index, u32 identity_index, u32 credential_counter, u8 attribute); + string verifiable_credential_public_key_hex(string seed_hex, string network, u64 issuer_index, u64 issuer_subindex, u32 verifiable_credential_index); + + /// Compute the encoded verifiable credential backup encryption key for the provided seed. + /// Supported values for `network`: "Testnet", "Mainnet". + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_verifiable_credential_signing_key(string seed_hex, string network, u64 issuer_index, u64 issuer_subindex, u32 verifiable_credential_index); + string verifiable_credential_backup_encryption_key_hex(string seed_hex, string network); + + /// Compute an identity issuance request for the identity contained in the provided parameters. + /// The result is a versioned `PreIdentityObject` encoded as JSON, + /// which is the format expected by the identity provider's issuance endpoint. [Throws=ConcordiumWalletCryptoError] - string get_verifiable_credential_public_key(string seed_hex, string network, u64 issuer_index, u64 issuer_subindex, u32 verifiable_credential_index); + string identity_issuance_request_json(IdentityIssuanceRequestParameters params); + + /// Construct an identity recovery request for the identity contained in the provided parameters. + /// The result encoded as JSON in the format expected by the identity provider's recovery endpoint. + [Throws=ConcordiumWalletCryptoError] + string identity_recovery_request_json(IdentityRecoveryRequestParameters params); + + /// Construct an account credential from the information contained in the provided parameters. + [Throws=ConcordiumWalletCryptoError] + AccountCredentialResult account_credential(AccountCredentialParameters params); + + /// Compute the hash of a new credential deployment to be signed by the account key derived for the credential. + /// The result is hex encoded. + [Throws=ConcordiumWalletCryptoError] + string account_credential_deployment_hash_hex(AccountCredential credential, u64 expiry_unix_secs); + + /// Serializes the credential deployment payload for submission as a "raw" payload to a node. + /// The result is hex encoded. [Throws=ConcordiumWalletCryptoError] - string get_verifiable_credential_backup_encryption_key(string seed_hex, string network); + string account_credential_deployment_signed_payload_hex(SignedAccountCredential credential); }; diff --git a/tests/lib_test.rs b/tests/lib_test.rs index 7bbf402..dcf5bba 100644 --- a/tests/lib_test.rs +++ b/tests/lib_test.rs @@ -1,4 +1,14 @@ +use concordium_base::{ + common::Versioned, + id::{ + constants::{ArCurve, IpPairing}, + identity_provider::{validate_id_recovery_request, validate_request_v1}, + types::{IdRecoveryRequest, IpContext, IpInfo, PreIdentityObjectV1}, + }, +}; use concordium_wallet_crypto_uniffi::*; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; const SEED: &str = "efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860"; const MAINNET: &str = "mainnet"; @@ -14,131 +24,155 @@ const COMMITMENT_KEY: &str = "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551e */ #[test] -fn mainnet_signing_key() { +fn mainnet_identity_cred_sec() { assert_eq!( - get_account_signing_key(SEED.to_string(), MAINNET.to_string(), 0, 55, 7).unwrap(), - "e4d1693c86eb9438feb9cbc3d561fbd9299e3a8b3a676eb2483b135f8dbf6eb1" + identity_cred_sec_hex(SEED.to_string(), MAINNET.to_string(), 2, 115).unwrap(), + "33b9d19b2496f59ed853eb93b9d374482d2e03dd0a12e7807929d6ee54781bb1" ); } #[test] -fn mainnet_public_key() { +fn mainnet_identity_prf_key() { assert_eq!( - get_account_public_key(SEED.to_string(), MAINNET.to_string(), 1, 341, 9).unwrap(), - "d54aab7218fc683cbd4d822f7c2b4e7406c41ae08913012fab0fa992fa008e98" + identity_prf_key_hex(SEED.to_string(), MAINNET.to_string(), 3, 35).unwrap(), + "4409e2e4acffeae641456b5f7406ecf3e1e8bd3472e2df67a9f1e8574f211bc5" ); } #[test] -fn mainnet_id_cred_sec() { +fn mainnet_attributes_signature_blinding_randomness() { assert_eq!( - get_id_cred_sec(SEED.to_string(), MAINNET.to_string(), 2, 115).unwrap(), - "33b9d19b2496f59ed853eb93b9d374482d2e03dd0a12e7807929d6ee54781bb1" + identity_attributes_signature_blinding_randomness_hex( + SEED.to_string(), + MAINNET.to_string(), + 4, + 5713 + ) + .unwrap(), + "1e3633af2b1dbe5600becfea0324bae1f4fa29f90bdf419f6fba1ff520cb3167" ); } #[test] -fn mainnet_prf_key() { +fn mainnet_account_credential_signing_key() { assert_eq!( - get_prf_key(SEED.to_string(), MAINNET.to_string(), 3, 35).unwrap(), - "4409e2e4acffeae641456b5f7406ecf3e1e8bd3472e2df67a9f1e8574f211bc5" + account_credential_signing_key_hex(SEED.to_string(), MAINNET.to_string(), 0, 55, 7) + .unwrap(), + "e4d1693c86eb9438feb9cbc3d561fbd9299e3a8b3a676eb2483b135f8dbf6eb1" ); } #[test] -fn mainnet_cred_id() { +fn mainnet_account_credential_public_key() { assert_eq!( - get_credential_id(SEED.to_string(), MAINNET.to_string(), 10, 50, 5, COMMITMENT_KEY.to_string()).unwrap(), - "8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8" + account_credential_public_key_hex(SEED.to_string(), MAINNET.to_string(), 1, 341, 9) + .unwrap(), + "d54aab7218fc683cbd4d822f7c2b4e7406c41ae08913012fab0fa992fa008e98" ); } #[test] -fn mainnet_blinding_randomness() { +fn mainnet_account_credential_id() { assert_eq!( - get_signature_blinding_randomness(SEED.to_string(), MAINNET.to_string(), 4, 5713).unwrap(), - "1e3633af2b1dbe5600becfea0324bae1f4fa29f90bdf419f6fba1ff520cb3167" - ); + account_credential_id_hex(SEED.to_string(), MAINNET.to_string(), 10, 50, 5, COMMITMENT_KEY.to_string()).unwrap(), + "8a3a87f3f38a7a507d1e85dc02a92b8bcaa859f5cf56accb3c1bc7c40e1789b4933875a38dd4c0646ca3e940a02c42d8" + ); } #[test] -fn mainnet_attribute_commitment_randomness() { +fn mainnet_account_credential_attribute_commitment_randomness() { assert_eq!( - get_attribute_commitment_randomness(SEED.to_string(), MAINNET.to_string(), 5, 0, 4, 0) - .unwrap(), + account_credential_attribute_commitment_randomness_hex( + SEED.to_string(), + MAINNET.to_string(), + 5, + 0, + 4, + 0 + ) + .unwrap(), "6ef6ba6490fa37cd517d2b89a12b77edf756f89df5e6f5597440630cd4580b8f" ); } #[test] -fn testnet_signing_key() { +fn testnet_identity_cred_sec() { assert_eq!( - get_account_signing_key(SEED.to_string(), TESTNET.to_string(), 0, 55, 7).unwrap(), - "aff97882c6df085e91ae2695a32d39dccb8f4b8d68d2f0db9637c3a95f845e3c" + identity_cred_sec_hex(SEED.to_string(), TESTNET.to_string(), 2, 115).unwrap(), + "33c9c538e362c5ac836afc08210f4b5d881ba65a0a45b7e353586dad0a0f56df" ); } #[test] -fn testnet_public_key() { +fn testnet_identity_prf_key() { assert_eq!( - get_account_public_key(SEED.to_string(), TESTNET.to_string(), 1, 341, 9).unwrap(), - "ef6fd561ca0291a57cdfee896245db9803a86da74c9a6c1bf0252b18f8033003" + identity_prf_key_hex(SEED.to_string(), TESTNET.to_string(), 3, 35).unwrap(), + "41d794d0b06a7a31fb79bb76c44e6b87c63e78f9afe8a772fc64d20f3d9e8e82" ); } #[test] -fn testnet_id_cred_sec() { +fn testnet_attributes_signature_blinding_randomness() { assert_eq!( - get_id_cred_sec(SEED.to_string(), TESTNET.to_string(), 2, 115).unwrap(), - "33c9c538e362c5ac836afc08210f4b5d881ba65a0a45b7e353586dad0a0f56df" + identity_attributes_signature_blinding_randomness_hex( + SEED.to_string(), + TESTNET.to_string(), + 4, + 5713 + ) + .unwrap(), + "079eb7fe4a2e89007f411ede031543bd7f687d50341a5596e015c9f2f4c1f39b" ); } #[test] -fn testnet_prf_key() { +fn testnet_account_credential_signing_key() { assert_eq!( - get_prf_key(SEED.to_string(), TESTNET.to_string(), 3, 35).unwrap(), - "41d794d0b06a7a31fb79bb76c44e6b87c63e78f9afe8a772fc64d20f3d9e8e82" + account_credential_signing_key_hex(SEED.to_string(), TESTNET.to_string(), 0, 55, 7) + .unwrap(), + "aff97882c6df085e91ae2695a32d39dccb8f4b8d68d2f0db9637c3a95f845e3c" ); } #[test] -fn testnet_cred_id() { +fn testnet_account_credential_public_key() { assert_eq!( - get_credential_id(SEED.to_string(), TESTNET.to_string(), 10, 50, 5, COMMITMENT_KEY.to_string()).unwrap(), - "9535e4f2f964c955c1dd0f312f2edcbf4c7d036fe3052372a9ad949ff061b9b7ed6b00f93bc0713e381a93a43715206c" + account_credential_public_key_hex(SEED.to_string(), TESTNET.to_string(), 1, 341, 9) + .unwrap(), + "ef6fd561ca0291a57cdfee896245db9803a86da74c9a6c1bf0252b18f8033003" ); } #[test] -fn testnet_blinding_randomness() { +fn testnet_account_credential_id() { assert_eq!( - get_signature_blinding_randomness(SEED.to_string(), TESTNET.to_string(), 4, 5713).unwrap(), - "079eb7fe4a2e89007f411ede031543bd7f687d50341a5596e015c9f2f4c1f39b" - ); + account_credential_id_hex(SEED.to_string(), TESTNET.to_string(), 10, 50, 5, COMMITMENT_KEY.to_string()).unwrap(), + "9535e4f2f964c955c1dd0f312f2edcbf4c7d036fe3052372a9ad949ff061b9b7ed6b00f93bc0713e381a93a43715206c" + ); } #[test] -fn testnet_attribute_commitment_randomness() { +fn testnet_account_credential_attribute_commitment_randomness() { assert_eq!( - get_attribute_commitment_randomness(SEED.to_string(), TESTNET.to_string(), 5, 0, 4, 0) - .unwrap(), + account_credential_attribute_commitment_randomness_hex( + SEED.to_string(), + TESTNET.to_string(), + 5, + 0, + 4, + 0 + ) + .unwrap(), "409fa90314ec8fb4a2ae812fd77fe58bfac81765cad3990478ff7a73ba6d88ae" ); } -#[test] -fn testnet_cred_id_matches_cred_deployment() { - assert_eq!( - get_credential_id(SEED.to_string(), TESTNET.to_string(), 0, 0, 1, COMMITMENT_KEY.to_string()).unwrap(), - "b317d3fea7de56f8c96f6e72820c5cd502cc0eef8454016ee548913255897c6b52156cc60df965d3efb3f160eff6ced4" - ); -} +/* Verifiable credentials tests. */ #[test] fn mainnet_verifiable_credential_signing_key() { assert_eq!( - get_verifiable_credential_signing_key(SEED.to_string(), MAINNET.to_string(), 1, 2, 1) + verifiable_credential_signing_key_hex(SEED.to_string(), MAINNET.to_string(), 1, 2, 1) .unwrap(), "670d904509ce09372deb784e702d4951d4e24437ad3879188d71ae6db51f3301" ); @@ -147,7 +181,7 @@ fn mainnet_verifiable_credential_signing_key() { #[test] fn mainnet_verifiable_credential_public_key() { assert_eq!( - get_verifiable_credential_public_key(SEED.to_string(), MAINNET.to_string(), 3, 1232, 341) + verifiable_credential_public_key_hex(SEED.to_string(), MAINNET.to_string(), 3, 1232, 341,) .unwrap(), "16afdb3cb3568b5ad8f9a0fa3c741b065642de8c53e58f7920bf449e63ff2bf9" ); @@ -156,7 +190,7 @@ fn mainnet_verifiable_credential_public_key() { #[test] fn testnet_verifiable_credential_signing_key() { assert_eq!( - get_verifiable_credential_signing_key(SEED.to_string(), TESTNET.to_string(), 13, 0, 1) + verifiable_credential_signing_key_hex(SEED.to_string(), TESTNET.to_string(), 13, 0, 1) .unwrap(), "c75a161b97a1e204d9f31202308958e541e14f0b14903bd220df883bd06702bb" ); @@ -165,8 +199,394 @@ fn testnet_verifiable_credential_signing_key() { #[test] fn testnet_verifiable_credential_public_key() { assert_eq!( - get_verifiable_credential_public_key(SEED.to_string(), TESTNET.to_string(), 17, 0, 341) + verifiable_credential_public_key_hex(SEED.to_string(), TESTNET.to_string(), 17, 0, 341) .unwrap(), "c52a30475bac88da9e65471cf9cf59f99dcce22ce31de580b3066597746b394a" ); } + +/* Identity issuance and recovery tests */ + +/// Deserialization struct for JSON output for use in tests. +#[derive(Debug, Deserialize, PartialEq)] +struct IdentityIssuanceRequestResult { + #[serde(rename = "idObjectRequest")] + value: Versioned, +} + +/// Deserialization struct for JSON output for use in tests. +#[derive(Debug, Deserialize, PartialEq)] +struct IdentityRecoveryRequestResult { + #[serde(rename = "idRecoveryRequest")] + value: Versioned, +} + +/// Deserialization struct for JSON output for use in tests. +/// Non-reproducible fields have been commented out. +#[derive(Debug, Deserialize, Serialize, PartialEq)] +struct IdentityRecoveryRequestResultValue { + #[serde(rename = "idCredPub")] + id_cred_pub_hex: String, + #[serde(rename = "proof")] + proof_hex: String, + #[serde(rename = "timestamp")] + timestamp: u64, +} + +#[test] +fn identity_issuance_request_for_concordium_ip() { + let params = IdentityIssuanceRequestParameters { + ip_info: IdentityProviderInfo { + identity: 0, + description: Description { + name: "Concordium testnet IP".to_string(), + url: "".to_string(), + description: "Concordium testnet identity provider".to_string(), + }, + verify_key_hex: "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000001e90ba8909d0806c237daf3c4d533ee12c3c279fa9dba217207c14ce949b67ec11723e1768b4c8d8d4300a4e7e2eed8abcb0862bdb11f5ad4a28d0a3fa0e85b689a994c417f5a306a3050370052b4a56aff95349a2c97f749a68085c77acbf1df399d3603227f82698010dee3cecb6855f27edc0fbc9d183a909835bf53a79ad6771aad9950d101940db4012776a06b44596134ae37e01aea98f2333083d4847e8dbf585c835e1d24072681933093fd152f8524eddc85554e1f5f5d463ed3f4c06b5d11c1d79124ab6f89cc556f44659e46590936941a461941e934d26354ef1c1dc7ca53175305b19adcf274a7493dccba69bd98ade4ea154f028f96d73bce9f9dbae14f6cc44e164d0cf4ebc850daf28f0514d8d11526495277066982c33d13dad43482806148cb2c8015637540ca0649c22de2fc3e11bbb015ce2df7ae3cd845b3eed46c50ba4beeaf9afb7834be1138eff0f0d2c991b33e4fd9eda1605f22b2ec492fbdc3ea4f88d66e837f044a10f3abd1edc6f54087cf2589215bafaf4e487d280b2b0e1bb4452dcfa5888a2f8eecd243180ceac2eba4f24b79c2c56c93459a8ee94b4e69a0ccea43def0ef34628acb418315867594ed1179d4e349ddc2b2b4e44f016a93152fe07bb87b7c2737db33b1974cc1559245e96df657abd22bcab4673a711974eebe760032096ee79865bb6b5abaaca2661044ef058aee26ce1cb79bdcdfeeb0b6ea47e731957b5b71ca9b30fcfcf84317351ba9a520f62695b688fc2125c693125c3f07d82536feb477d721eb6ee30eefdaee074434a1de3e7a40b03acf9b42fe1879e7b1411990a08227889821acf06b88c7e086028f1af2adcbaa74eb8936f665b3fd011e08ba7dd888ec898f10114b4659610d1409a64112ee972b7b1f089c1c9cc3be74e6b08a761ee18d0bdd063c4b6327123e0bf9e8dad20dc8fb048e06caf87adf3fda5fabbe8398394051765b7194b28b74280f9d7736a2fdcae133cc29f0466c6edc06df087ed29ab703dd598183b75bbed6b0a71947fe8a18fdcddcd49386558911b1c1362e5b8159ae39192e791e7254321545595abd9ad7e859e80ae3dddb14731a981599f8e7fef5bb77151850fe343f7dd91db35cd00c1d02e7101c5dbfee9d9562e9200ce13e7638a5a92a723299986d22e9699a90c15f26151417ad770c8534a1b54f29dd3b64dc2a8f02374337344e5aca3d292364ee245476def3fb704f8bc065a7a1a6a79ae2387bfb80871460d9f3052e85f7ce042c104325800433728da6f84790b049df5933a59b3c72485bda556b3360efc0d02c91abf672284bf7c77d0638667e13a3dbe17d2793395009983fba2902e7862de908d22f5562eee6b5f7cc59f10a1afa934a3510f4b032f136c78a56dc5d8c1132008808473be726b698a91c0030468b64366026ed779d1a0e9d65508ac22f5800406f084c7fb4b04ab1c4af3dfadb9e51cd7650fcdac64ac2f8d8f8f9e483e8c44b9fa93afcf36c2210110cf04b3f154069abc50df6d8bf6a59da226fc7b329b5fbc921ec66d87159665b9b25ca49712f9067a8dd901342d97aad36a0129336b9a0e51d1174844cd719163266c8b30400886600e9ee9804c4813b4f03072f0a4dd57915f15ef4d8208e609b0d08126f6aa95b97096c96a0b125f097874dd2fb8428b930211575f316cabad5f14432b57d7342f883b120bf2a134c36d45960a9a9a68b8fac820f03e209d48197d467c416fbfd764868c63770a038333542835ad8db288a9f99389e455fe1ccb74a53b00a6665bfc206a9da0c15fb8b588f504ec9675bbd75a223f358545b8d93f31b6be902408000476ed980e7a5e9b13046353342e3081dba9ae0af25e6ac3e0130db1714a2fba6fab008513b4ac1a285c10274cbadab17eb6fa27125f5c3c3752a8ae7f39cd943a7dfa7ebb181cd300a3dc6a69d11591b91d28eeeca9891035693e0b71472015cd1754deed74de61c04982153417c3cd0b060632c8faf7c3dfd6591867dae68923f9a5f3d8190000001e93caae1dc016ba6b674e97ec58366e5d9bbc91020c734f15b9bd9454a53f50d081f4301d80c09b30a0515111a1089db200e59fe3377513e0f2a07d7059854854b9d7ad2febc90154d4191f235e1db4e3fa0690312346c407a960b18723ac568f8ef3b09c3d8574fe3a118815ff80391b71145f822cec0921006a3e2e248e75f4077a5899eb5c94594ecf5d53c9461e5d0697fb336d8463c9c7cafbb5d2f59f4dfe3fa3e93cf1af6bfef4b862e2b3bf6a29f456025829b491b393fea7bb7859e4ab776ecf95abd7fa1e4e03c77525af58d297be183a9437710f72b35d7fe18c9daae5a0b0dedec5d91eaa1cf779044bd304e9b5c3ab9ee20d197a057a328a71326e9301df60c729ac23f4280245c262de278cbd64c9c58e9591044943c20dc51f8c072f4121098bb8949ad7b8b042b98dabadfdee95a825f106690a736f46d518a7db5ba13f5dc6a3fe96eecb5c9f138919ab329cca7582e448db8d51fc30528efde67a4de4ceb1df40d4c3248bf1bb6618a0411dc205e67f192727a82784cd77839b3f91e1c0b96858187c931d78a79ea1e8e63924171d440eb117d9e5696dd9182a24d66c1fc885171901016f92a23a136b07b8033cb163821ea2ceb6917f36a17fde1bf17fd299f0bcc3021f012c6bbcff17df0e6bca3a0aaee4f298161ef78a6de30c0b14bf3623d8fb980e02610d31ec22ce6449f19df75acac7671f17ca75529c118215f3a7ed450ec71e7175c013de7a423cd96425d10024b6470b7b3e90ffd8ed12a19bd0dc61be354fbdfa2b6f42cf7fc427eedff7d90d432d3fa2fca09a13d0b855f0824c081b8f3010e53deabfc255ff6be14e82778e8aafe91eb222668b6352a0761e424e16675b95cd15023ee7a1927e61540c19f32177a7b564834df3ba08f6015b85f3abfd3589a88a6963771f7449137d8d58a98ab1411abdb23196352eca8d820cdf41a9bf4690e8dc37e683a5739d82a933d1295797ea30d0cf5efca3a26fa1eb4c477830ba93d9069eb7477419ab1defc6d57deee580bd707388f971e9a6359ed03b6bbed63ac29f765266ccf6f74631b60088c4d9415eb759611ea07df121917596067e74558180c9d7f77cbb571a29bdc892a1d8431b195e3c8cce609a221a2fea32cf948d5c0aeb37e3a1f1e01668832b1e6dc21435926f28037c079b721115556c5eafe1da66ca9ccb63233fa3e2bd0fbb4718b2d8a904f05a775d60f9f6e7c2891078883306208ebc8bab4c61961707bff16b1ba070e115dd068837591b1d689547e80ea710a954ff5f8a0db3f6fcc06c50e47df20e837cf793f67bc8dd66a060e0030c93b5e1190e7e934e2f85cdc06fbf5728ba85b82aa80eac192d900a4f4881352756e3a86dc9164c82aec566d7dfcb7d6f61d6a6cbf5959b2dc0b5eea15874c9d7c619e227f94fcb235ab786bbab5680500615136434e52cec54cf0568cc57e8191e9379607878e8925e5774dc3b6ecd36b7b1ce6061719fc4649c4457b8a73b1de11df3f475d7d763c6e31e6f1311273f3dd007b5c36ae9641bc1c1287748f9f27a0a3728c90e2ee092c747b96fe49f2e4b438d14dad21cfee9ef30c4d29365ea7c045bd4e1ead7d9a78525d33c9d3ffe81b81016349ab583633a3343f73a56da136996d1fab654f3f8143ce854367a9958e7c7744bb6d3ec0f644dc11aa2e9b91f04e70d89202fc66ed4567e583e0729dd453ce8284c9b077864f2596cbae38c455c492ff8353df4db5891e18ac5fb1825ada5b80e7ffd90ced8b9395149e5d96731438ccaeee7bfd05cce5c9e220f96eef7e15eed4b5665f94ec7e6b22e24bbdc0509be51a5128f5644264641a88a8949038d6bffc860902874d29495ac9c11dae076eda76c541937d07b51f5b56623f6918d4b11dc2751b964062014827cb8480d82bc7802b29b3c86aab59e0ad77c9d1c265bca0c63d098d984f20684ec5897023e6c19cf06a5cc3e5f5ae7c7e45900a6485836207a4454128e9c6eb86f151606321a68a99ad4d0e1235489d0c62ffca462e5be075656ed3bb4788238fb555cda5efe8690192fe7319472655975c5e0ea09b41b8696df8422fe2ae259d8c50709640a135f660af6338b0a18b2e948f4dd2fb355dcaf097d75cd74337c930efba9bb51d40f95424b091cbe4be6447ac28c3540b12ca3e35e076c37b02b5a1661f6e8d925413bf866e5f5316d76706614581963ae622cae906d1d68f076eebfd601668e0c87419e74419f3b0deeece71786f2915b64fc1c97779a953f7cc2770936577581e3938e6cf85f12784beea404abb446754c2bf5016a0ba7862502a587d995f08bd284ebb2f45aa07646bc6e1daefdc5047e2fea41ecc954067ed9e23902d6a8033f68c2b2232f0a9ae7db7a8dc53ef956aab7e04c2b2ffbfb0d031ffd36bf6e4c081a209485bdb755b4a1b677a515cebda98035534df3ca7f1c1127d15acde5c75b77f2cf218df18b8566d75b30488179b18756839b2c88f4d3dd84ea1044abfe791c32ba2dc5d0d7569a9600c58286b3c2c2c475b9bb2138e47895139452e92a941daca3f2bcaa8c3dbd7dffa0252b3ae7421597e385ecf9a036dcdd86e6a1d817f2fc41b1c73cafdf4805f28de37e32df6e8d0b0acc4e57c976c536023961992d7201df5d47a07cd8f8a83faefa3b55443029578cb1ba9dfdddc2acf9172d141de04505c137098f9cf19e4baf82447266861182411a31a1f02a1b418b5330fa225803fd3f56e868dfb17da635f98cc18033856da8a3eefae0f6541d0517ba0f28169521e5d8764d69eda3fe76a7de8d9b45e92c5f16978b67e44ff4edcdca2a4cdef1286e2ea9fbbfb9f6c0a432b45867803ef97384272e1f71a9c11d434a6b7b7448bc64aacf483eb8b496560142ccfdfad1faccba50bd5ff12fda1704622dcbcaf8dc7b07c963b1d429d4da7f5b33903b45ff4020187581edfdb67132f85bf08b133553d708d29c3721fd867b5e170eb70e65c9cf5b344944df2e879760c7037e39383c24028e6648f59f092b5bacaceb5d282f31a3969dbeba350cb907557553e26a7f2fd204487b7a7c2099db4a6ff3e4621974985165227c087c866ab18632b425c94c8a9a297f1099923c2c6003181c0c788b3f63584e9870deef0b4a95260af3d411fa127c9ff56ce9f3d1195485e15cfba7623a21955d7f0d2e5dc2622c4cda3a23e4c9500ba9b46285a7cab36a56c01e4aa371be55c99dc14cd4d386cb65bbf82617b4c83f0574b99da242c6dc814726bdcad4708dbd76b9b42dc44325951b40cfedbc68e744b3f95ecabf0383314764aecab1416e449e00ec8f59552f19e2d689dd3c5d22565f20ed63ab97d95c5694b75858fdbd20920d20629c638eec08a76feb079d2edc1798f3e34b3d7648571dff2f8a9e653d173f196a4bb1c45987312c77b2b41b2448677953aff1671763f2767d7ac7e7a2a605934d751a6c9dbf9351eea1975cd1104504ca02656f7a8b9e3e886768bf4466b7b3b2f64ef89f8946e88b8113314aed94ae9058abb2b8e5fc211d4363b34ca026e6d9ff052ae9e21feb7b23964041bb763f6f93d5b0b0f5499a662eda86adeb0b98d2c1b8db808dedc3001e5af49abc66df3ecb1134277480097137d0f1641d7f7442c78c38079626f7a7b3f0f04e6094681ec8eaaf4e1b2a4d5ad2f5909355f588db016e831eecaef81e05f465323113da5a5f5056481a3923c77ecf237a4a18404de91a9eaace74a2dcf2d66fa1437df048784c9de024ef674e50113f18b100c48f6f4e6a2ba88ef3f8d80c6aa9d08eeeb3e198c1c483c7cfee4b275cc2af6886c85dc551b2e7063a2fe48909a37febdbd5effb4665f2b19992356f92af45121ad6ea3889ab34c4d2b56dee7147090cb7739b42291c11944868a50ea45f671c8fca418e5c396f02eb3e8082f0ebeda069f8518294211aa9e6efea55693e8646a7f214d12de55f0e5fb889f113d99c2211c18e6258e17789b5773479dbd7b905517c375f23d60951444707a5292fadb7da6f53770938decac8580c8584984106e9bc13d5c307e5a78af4e8093bcabfb5e8a2eee50023ff0ece77cad282f2fd14b505b339ab3dbc87eada2a6e6cde041ec5d5ddb32fcab2d94e1335fab55a5d14973b4d73201103b283115a1438d76265dd7a07c6cbb30cf6329d57503fdd92dbdd4efd3fa5a60c57b7d39780e6f846".to_string(), + cdi_verify_key_hex: "2e1cff3988174c379432c1fad7ccfc385c897c4477c06617262cec7193226eca".to_string(), + }, + global_context: GlobalContext { + on_chain_commitment_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a8d45e64b6f917c540eee16c970c3d4b7f3caf48a7746284878e2ace21c82ea44bf84609834625be1f309988ac523fac".to_string(), + bulletproof_generators_hex: "0000010098855a650637f2086157d32536f646b758a3c45a2f299a4dad7ea3dbd1c4cfb4ba42aca5461f8e45aab9112984572cdf8ba9381c58d98d196b1c03e149ec0c8de86098f25d23d32288c695dc7ae015f6506b1c74c218f080aaadaf25bb8f31539510c87e8fed74e784b63b88afba4953cacc94bceb060f2ad22e555cffe6f0131c027429826dd3a4358fd75a06e8f7c5878791f70384a7f3a90f4b7afa45fae6e0fa7153b840f6fc37aed121d6c51225c56d1ce6bbc88096aa3f86e6b3517daa90baffc69b9eb27aaebf87f04ed091412547ec94aa7bf0c8dd826e3cdd621e11dfef6fc667c53a5abc82c163efa51435b3be74f47073511db07c1af7229c78ec38554a3f7e9fa38499201d4fe6c80e867df75f2c540e57d2e15f9f5e18f73c0a804567494aba85653f57f5f990111d680400bdbee2d0678271ce93a119c107458b8d4e557fc870de9cf0ccce6d34a216a6ffbec9581349366706377b436261200834fb2654ad5cd34f5bfe3f3658ae94838ed38033886844008143d823f7e49db43017655c53b983a883948d7401f26ed14daef0dc46da30f57e187ca8e027063174a412cbfc8a7b606b41fcf7f75b698ce9115afe5124e8d72f4b34ce839742a46885ed60af26f24f8d10d46621d78b5772b0314311ed3bb627e93bac47e7eda5ff4d2ef5f98ef7265677a382a1f7b8f9a43d1e563b66b6de94c52c3c87169bd19b6e884c6a28dd6f51ba64bc36ac07926a8d91a64e88a4e19b4c0fe0db8b76c9c99bfcddc05fd5630c54bff85c041fea34a630ffe1e6313bf039477994696db0596f9e2522e04ffdb530147780099d918ee47031db2870c00b25ba6d201b00ef2459e73f6a8219d7a7ff96833bd1c8a3c24285d93b85a321d1b6e175f2d9ef111a7304438b5f5874f064539d27bf8bc46d8e2473d458f957fa206bc417ab5b8ca4dd5f0595e21063c1bbae14f07bec6f12d95a05166b90896f33aa941e0a057989dcc1ed77430a3e6f93ab16f62598b9816dc203db6ecc7fd7989d2763d4ef72abae3c4aa1e1b9c9965f8772a1be640a18486dc0804a2efdd7175316af3220a6c10db0ac0e2913de42c44f2f54a20687d2b22c9636e0ba8d09747f2229ee6ba0fa49c4a328f2ffcc5ff462ddca0de8b1d13cf85ad590183767a9ae4179b3c617ed776cf14abad1964d362747492547359c4b65e78f6dfb452d5a559ac0c24b8affdf13256e43d3213bdda0c056172771cda382f3f31fecb82ee8dc81a48fe74467665c4e34e07b1954cae4ee97270b94a336779d27646348ffa0eb52663aa60bd8b7fb9311d38a7fc1f0f2842081cb81b594dfcdefb44db1caeeac7527c5a48e8a8ecb83de0514ef184f2a82c4110f9f64ea655fb42218f95e27c81a0adb3fa7b441b71cb15113aef7c318d73f911688d34111928fdff181dbc014be35cc7e3e5bd75bf767686933931bab272eac577558b1314206d97611152ae82c1bd56ab92a4378dbe73e76a9fbd90154bd1a8c73c681f70298d15f1574d7038521b4e9b9cfc0d900c2d74cad594b68923607a1b913cb9484e093ab8ef2020ddbbd77d041581487f41e2dea13832e566325f4fb0f9934df94ee068c676e475251659770b93e0e32c7739d3c430db581308817ba091c56a5087c6248ee79baddc3c233e77b3fdbbd73f1a4298a8277055ee32a96b7e83f76e0070ea65c4a1784e9e281fa70fd9771c762a91c7ea014f60c87d09687768c9d0c8277d84e8af2eee505fda2967631a999619dc332d98bd45d40458923de655769aac754bf9081dbe9318b4880bd325e6be713f3aafb65d8e44b9143acdc7f2ba7f15290c84225c77111e0629d0f82f2f367d2641d0c3658fd54e538286787d379252876f71ec4a96ca72fdf8b156059ec50131317b9243eb71c5e84ab6791303ee0ebacd790bb27767701083afa616413b1d5bf0347cc4199943c938d74eda87660dd40405aedafbaf54b1e177117f632deeb6a9bd5227d9b8ba5693625ac68ed7d1604ccffe73e97a45411b99c666dd5eaa90cd021d05f12f84acf60bafe0c98e8ae213fa65c189256c79afb4b3eba970a8ad4e94374dd6f9d258b8c86335f36481affdcc9eb074f1ace01fae937a7d1a92279bdfb1081e029103e48877e4442199593c1b946aaed494885b0bd9d67fb7abc6f41c0f7574c3cc007d29a4ddc0a966270f5432a47e3e5563061b158eedef8bae522cb1f800c6d5f0a85a4e24a68a9bddb99e408d204e56cd994796bd004cdc5871798df2bf068ac48271290a2b3b1f0eba346f8746c79ce2389ed3ced67bb962fcb50876b7a32afca94216adadc0aed990dfd2b558aa5d8f35aa773e710f14f1641ff7eaa9ecab43ca3dbf32e846a90c8a3771d378aeb9277ef864377f7ca79ec7bc9e4b373ac890cbcbcf7c33df157e0cab0fcc7fc4daa2599177bd9d6bfc4be694a8e834be081bc7fccee530fbf6bac5be0cd1656b4c79ba6e121c39640cda83f81d17324da795da00f9a05317224ba827cecea6cbba1459df4a5bf6b84cd009d5864d44e747e3f30866a7c261004f861359e717c414a699461086e9136e29f9973e5b3f008a77fedb184e6835bc50934178b4bbd8bc9ecbb10865789d063ee7781fb2dc7b9b641b73fb172c3aba915c420c1c72cdc999d1fd224bf4d3d8a5ce26fa36100533682964abc2e99368402b0c518df1b6af245837eadb31a5616884c5474d25f57a228ed2bbef64d5a80e37cb20385a0283072a3b7a11b68bef323ea4804608c0eed5ea213caf6feb183558d56666f472a9abd10ddbf49650c5318109f9c5ed8fac7847644e153638e29e5f45dd00da06c9b9aab4af18eda0d0bfdc48ae42efe79d4e7196ef07c4319efdf042724d9aeb2c13b089b1fde77467c32cdf778749370e2c3b84a466bdfd1aa00ca8ac85c0eb29f1ccba6953a8bf877802ca3e3c31ceba8f9ff0a8684b3b118ab11a27182b033847baa372fb6b4c88e9177b8b131cd69f6ff5ec347db17215fb9890fab1c4d265d405ca74e249fafda004f86f2a5c3fbec7cae6afe6c507e089bb2d47a9e421512c016b99cb1cb15a3b8e75f2d8ff4866731585c686871691b5ca89a1351f6a79b159530fb5825f87131b0bcee2573d3932d223a358fcf08c8d4bae92f26d7fe35255c7486484bcb26693174ec2688abf39db5f091c8c09ad804f4ae42ca8b645200f27629ece81c6b5d94ef7646cbbc4b62a716ac6d2ad21f5a7e2297ebed15f2e085859a1002a9e7658b0d0249446ce9e769caba71536b9329a5fea3a15d52344d42d868b0d92a423fa44f8a6c4aebf50fb496c62d03598de39f1fbdb29e098c6a2e2ca59cbb5e37eb021594463a72471683117b957890f8307828168b1a8f2066a5e5f2c6822589272f259b012822b8be2b15c004a2a69b66c2f5ee5d81aa9a7bbdd44d059587bea89d021c734f62828382c9f13210185482694f8b30b46111d426927ec5f10b155f825d852fd995b9c7ff27861f7fee8dd2b654473198504cdcb53154b29f323ac1adde8510beabb8cef6d1a927f2dc844f746edeef9b256ace54dd8e3219b265ae958ced6593ea154b8188bf58b72ff05c036630b6d55141c4ac227a4d966fde1d0ff1ed9748c66127feec86e7839d77c1c56b46e29d2033a7871b4b0e3c53b3cc4ac49dc8eacd0555b338265be1234b81259b3746eae71600c16b7f4bc692f0a1d3dc2ed855c9fb11b2ff5fd9e6361a82d42bd4ba00635d974fa1214aa8da9180cb158347608e0c44bb5b77397d8b233df7be91e7047d28d18ee66472b98c78091eb95760c5b381d6050779ce65fd1508af8df81a3385f185f7941f0b7aa9bcd6023c6e4db7ac3045aee58f3b2d307a5be14e2b59d05333949d9e8dafbe15b671034b7c629c1de1bf560415cf72761d4d7abcbe3b762d9a66b1b630b75c2af751f6a5d37b43e84139aeb418e0b10dec012a32095cf7305bda79bb3853c3cb1fb3c8447ee4306923b6b693020880bb11df8c78f3411f176b2393776d0b9b3ea7eb0cecfaadbcd8112c4c4ad9960001f4d4df210860470a4c89a0fb9a07229ec3476e1221f490ff80314da398ffbefac3a376c53c0db82d6785bc3d3b0cf3ce4bb54cb9b6fdc29b01bca2894debb7de518a61cc953d4f92a9c52d427e2858db633d042ca14028ef771d9c8c62014676a30ed9d5f9f51036a823a388ed0f72321a13dad31bb28fe383e456361159490942382ed3e49f1c420968877a42b7bbd8c3c9e78723017400dd178f0f89065072d7ef5f5e9baffafb2ce55fe7b35a7865957260b1789e3fddc5bebdfc3301e79b5077a92f6048121c557099c329bb270cb90ad47818398bbdddefdfa6b9d9256b8194f51b45c69176d5d7deb6440c9a81bc5ad0c8b2f31720f071a8ddb7907fb161ed658f890540ac56c506b7b98df3a45f6fc062698bc86d3cc1150db00d702b2d1e3c8eae552eab55a7afd5a5a3be2610867cfff07545c8227dd26c502039240cf00ce2e91b7bd7ed6495980639eaf919635c82c5073acdc21aa275ba87cb7280d0c6321c6a4021b1f0ae7bce23c32238b3f486ce27434721df436446f9ddfb9c66bb1f4b2337803850b2606d8954268eaa0ae060f8a6da2a0e7cc315c1f8cdad4e11bdf5165a2d72ad34f976bfc41000afff158f31e9632b898f2e9886b8b92c81bf916861c62f07ea326e94ddc4ff9af51fc0420b0d3c57fe0d14109ec90a72de74147d8ae1eeebe6b565c4ed81ac01d49b0e6a9551e9fdb7a9e040b4bcbad61fe627983462106e1c0b59ce762ce89b4b1100444048e2fe84850db19d63522d83993067cf025ddaf796e2f11d76da5a2b6ef4a409720ac4e97f7c75973584d935ab0e723f415a5622b03ece90173ea58da495f402f5ecc33526d251da5d2eb5558cbabc1634e8aced49d7c3bfb514c20f7284e8a902855b2296ae90ffb84bd0ea96aac846b6c9dd8ce92c0f9d457ab44a381046e7cd50b4958bf4b854bff340be4d41452ae1e3e19c82b99908c0a45968e580823450ffcc67561dfcdf446a57ed8d032ca94ec3e8dc0a181b9faf478275fc353ba104d5c31cb7338cc4c2cd445ffed7025f576ae84e9b08311b77490b6111dacd7e3afa920193cded42246276cb999d5d85f594352592002efa2d34c387fa1901cc0f63c0f6ed141c7d2a48d5bed031a204cca98c4e26ab7210d4ca5ba3e20df6d1b0e1daa69216b3e1ed98f20ad9f527efe75a0e7e2067c85383b7a614945669b442e1f0d7ee07160902ba9ac292fa0e9c62bc1b0e848856aa680898c4769d965441a16cec21ffc2eff717267cf0f9d4e7139b764c72826309e8e0ce8965c6c96953c3ebedb4ab4c9b35c32b7b59f8715f7c790afe8a3c7a09965192baa75262a16650cfa1706f4cdb4d34043ad1d6a53898164d049647dba7be95b7c620c750e3146155f57ccaeb283936ba6b9fe6a6c6fc5fec11b59b2d12da76075e022a2308fea5865ab5cbc12a4898de2204bb218cb6757d1d3d380b488a22e4dca9c9b1d1e2d01e7a9e707a6c09c323973dd639c7d48f25e4b5b5e95b4394a1221484d337a032b34cf77998acf78efe6d7c8c1ca4f83970bd62c0a790f38e405827a71baf31a9c89892f6745e2d29bc13b5b2873c0581df064a96bf64eecdec5504a75c7e03369662e0ad9f6c2d38c4da79b82f62aeebb69392a78a2cf6fe5a1ea8627b77d5ec828f4d445fbf3a51987023218ce4f944f03041d01976a2780fe632238a828414364b8b98aa8d709bab9c2202b144f9edaab822c33d5d8ec5f62517567711cf25c890036700b7f39b130335b4b8d14d4e9ebdb6c4fc015027907b931fe5cd99ad87976e8181fa284a53185f1e982ae24842abf28f23de420376b205222e5067a2ec53814d659485327eec748b821d70f114fae08ba181f320717dc462c88a15370cf8acba5516c093e13102d92c681bda73f3ab192976179dea08875abdc054e389b309d62a9737e05edd489469f63497da850450e1f299aea08649a9ed1982a4f8a3b251fb8cfb4737ce650d125199d07f7acbbd50dd65ab973be9684ee248269ff200e2f36118cb0eb9104b94149f9c91ebb76f9d46ef5c11b4b78bf6331116110dbe6bebb7169d3c637839bcf89b114ed9d765cd6211405e2dbbe78a4ad608a960a051097a65dc0620a3d1143439c4e04cc6eb1453e43904dc9f9db084d52b2d251dcb1d476141b74c627fbff849eda5a0abc85416b97d9888dd3cd88d33104e8f0fe03ade560dbe4baee030af797fe68e45196a2137d79de2ced8e6c2771d6b16a74b2c1044e9d381a0107adb870af01329c413c0a29b88d92b3a3c4a117424e613704917bb2aeb343dc22d96b0831db78b63063f10527cafc6731b32d50e4b6e39f45595cabd413f62117244ffc1d69122b5b1c9bd864303c46cdaa1e76a97448ba5ed266859a744411e29d1ce4b78a037d99d337a7b2f74907b591da8103613a19bfa6cc1f87b9698912b85ddb0ad0d86569f813ed4dfd3a9edfc0803427c648ef49724149e49aaaa98279b2ea7d25e3463be263b6cf518e9bfc456e993c2a3806335f13deaecd3c052e8604a08c045175d044f7544291dd4acce9db398fef80ffd0fc75b28886903d2d39c133a1869b8429c9ec58069384d8639e6d268849bc02f863f431f574ca988b84ae59427e446891cdcbed74a72e2b60601ba707b2c839d85b33320a32c1b1f7514510f70d08c474a7b6155a0ed004a23fee27c6aad072c0c751474948a1397ced8f04c5b50b4be2001f4f52ada7731781b69ed93b7602441344dedcc359c385a897532f3475764e0ed3c80959cd95d47f958d237f39694cdad607b966b0d2b690844646b614fa9fd4f169fb4de7c2ff3b4805402e4408c5ba5be95cff6bb68c79104a58c11aac0f999ec307a230eba3869f9078c453af1539a9cf5f07dd18a10c2f2104a2007d52d55b8ba7e24d97002c461c2f21796055179f403a012554b2eb503bd7f62aa3542df13ce9b520c81f364b7bea0deda55e430ff52e0f7408e43e4f7289d93b2431ef21d80e04ca57538dc17b6a7b2f1729cd52ccf7f8b4d58fb2fbaef5c9b3cb38b34b179742e686b9fcb884286cbb9b35d70083d8dc6e6e9f374d473a79ffddf8547bcfc05d5da6250ff982c8692b51e6b219406d9eb1b39dd2adc8de3dd86510da7c383041ed1e87e5104d33478bb8bd7891e16c72eb23029dfb8b90c98c15377d0928a36a65b01b4e83e5c93e5a804a37513447ca0e64a945c0807ffb5f2ff06f270e9080eb0fe471b4241a9f66235b8066d425ef09e6d46e19717747d80f5a16964690b949b335b4c20d3d15a7eabd7e815856f3463565654eaa243f7a4c619289212014d1e1effd99390d1feb0d9a9a0b101a298aefbfa703582c5c2f6470c673a180cd6bd9adab2e6d044ba3f130e47b7340ba9a358b0600d1a8862a9c330e6ef1782ef1c912a4a60db9522cd3f1b941832abdce920a6bebc950d29e057d6a71e75f9056d601079109bbbb3b7bbc46b413934b902ea7bd0ed115553de7c51ce60ca100272221a880d3d2bb0b49f5b36a246df30b7fcfa3e924b5e0db2c55038289b1e266f91d504c14cc3722881721f46d864c9f5ca7c736ade9d7196c9fc59a4740d59d0bc1cc3845ab33c8ee7e539f2bcd8d7295e88803b1a66552fbe133a585962edc24ef8b8b3ee88d005c5817c9e65249c554a678b71fa1e6dabdea92b1ffcd004e16f289ff1758a491a2ebed7c070423b4dbbd61141d3357ee08bb9be71663d0d2ea59f4322ee39348fe8af117fb424ac49e362ccdeec78e99f7f2eef955f7454ce38c8dd963bc6a43bcf476514a5951637c64a71dcb65351b05c557d7485494cbbd0c206acc04002ca1545d1ad3f70cff600aeffa021fcc63d46b5128d466c87475a6b420af9e4648b3218449886a5f3448d2993609e7055566430b2e3f3d09a22633e19a1dd0418be9f99cee8be8336f575d3a55dc090deabc6786fd0d210b7e9577d69ef79beafbb541f96c6a01c9344653d7f84cef645989c9928b1738b4fb901dc9b9c29f4e0940fadc8403262e953ed8f6d772f1040b6ed52df64f7bddcab26bd6f5575e325fc81ede2c931f46c8cac6beb889961d6b2395d711261f9ddfd6be95e17f1aafa506e7d14bddb132b0821e7b06cc8597c8133878ca983bedabf66d0d2abadf4dfa38015d3e0afe9323c792dfccda5544c17624162ef0554b5cc6d77362208018717a53f44c2bffec644467d6b2bdddfd1ac55973d05edebd6e24c41223b8beceef1ea63410eed001c947429b115b864d785de238fa0e68411e9c5de226bfee699ab981ea86a67935faeb6f571f04d6dd1b73920a4afacea51c89a5561532cdb94a08da8e0bce1da552da943efdaf4a9549984337f0f5c4b42fd81a4f545e00d86f41ac6d54eb554efeba20896f3e1234f41c285636524b666b69a21bf19318a618c95a05bd4754318f696e0671662e9b37cd2393fb709e687a8ff171155d71e6ba436857d522aad38d34382c5b9f2c8e5b94464156cfbbefb3e390a935dfce72b9dbd2197e241b68cd9ef87de8165eb0515ecf534d5bdcd2b6a1540c092e26d99985085a95e31cff50452e89a3de519a0bc9494c66e77434242ef17bb0f4719b65b10cdead9b2e9072c59d334cb43827482fb92876e6fdb66dded5245e0f4a8aa6ac7bea417f3feddba61ee20b092fda1502551fe6cd0f14023fefe4d08767a0eaae297faf7352badf5ff9e58cf4d1fecb857ac3b9e83c90d19345474f82c074da1600172969606801ec567df6dbaa2df8a4380d3d500570b8461b2cc8511e5d3a45495b3bbe014440846892b11c3248decfa722419cd30aa94d9209872872820ad1b02b8abbce3b26e31aae5bcc816795d0bb3810c1e4f6ad22ff0a00ad709f93c3f63a9dafc2eb7cc6c8d986808e165b25dbed2af7ff5620ab09b3aae0928ea163fab6315b7885448f5579c40c35ed50340bef058ab56186940cb5fadc5b1d093c1a82ddd172a4d5cf3efd5c5297db8dba62e1a302127b7faac192a1de5f8225c49607768a7561dc138b0c4d9888fe68a5e38c125dcf23c7d9066c34c7536226cf68e8106c9eecf4925e48c6fb8286c5e4d48c136550d179a30daf27017aaa890abdcab582f67f4b7117257ffefc0bb8a8b6bab8c68894955bc13c185b64b5e790483145915881643222c50fc024002a7c619ac5561bb72704c2d46745ca5ee77311a3c22750ee8cadcc77dd037db697b939ce0e8be03f83c82948d491b38289483c8da893ebd9384f83f1ce0148a43065ea4e041c22aaac15a1a82dc6688c3f73f86f97c97ca7e259cf588f8ddf2de829970a0cc527df4d9514032a4161e3175c34dc46065c3a1f4c70a07734fa9bf76a3744d2764df6fb49dd8a97d85c62890d6cc094862a34ca78558c47c263fdeb1dd5dfa9b6c0ae2b3cc43b5fb742049ab38e87f9d50ef256fe3187506f8b462ac07b85f2395089291336d8233db5631cb1afc311f6b1bda3a2b8cbeecdf46085c5f9c0727f40c64681ff0adadfbc742a8a70d717a00080277f8fb67074c98ab4cdd2dba5b9a6d3defa9b6024bb7c452f19b74dc8363cc33b62ff9d5653f7f069723f1d6d7f7fdc38980bcd01d1867c2060dbc75a615933c2830e5a39885185d74f980c733afd19a830b12dadd006530b1514b5ef61de8b5b27ca58b9471029edf360e8bc4cc962d6ab28a1dc2083c5b98510f4c22f4577b9c416b4072f36b298ee1ce8dd8111c254340123e19509c179e4b71cb268381af7290196666e055a7de2715e6cc173bfa5af642a3fa942c85b07fac2f9970bacfa03e09d36da2f80b4f1a379e8e5b43d9e2890d551e6f37df150fe561212a3b0b714d13b46765dcbbac3f6ea01c976cd366da0de6bf601eb17230e6f3032f9a476ceb75d7d25ceab9f65bf93102daed84af1c7e336daf571bae47f98f74214d93403f55adb50693c9cdc1a2144f1544bcd1e441a34a8d2a2f48387130e569a9f3053d510e2bf379df90ed2064ebd503b44a2a729a19ae1af257508dee6133bc8cb0af76690be0b41248f3f447adb4679cbc91e93532513a9cac6dd4a587ffef01c202f6e46c3129567c833b5db32fb36054cef0ed13530c48cd3bb887137f41902fde6e4e472bd6e38f6a0992d363a0470a1f8f98ee17d528a0b4b187d6b0a931f2a0b123d50827d6b50797da8029a868f97e144f7586afb6b394dec7ca89d85be8a0ecec25863eb9215704eaafab2e277c3256dffadc6a99fb3bc94b4483a3fce40f29ecbe39c223a9d99adab90973c65ec64b9b9ba9f1d68ca967d87df7793323d898fd09065da2c6051308cd4e535634e235e6284fdb3697bd285eae40d57b976d7ac690e6a96b193a9c3ab3dfd771efce5cfaf12d48abd69023d7773ae998e7433600924435bc89163b7a18f7bada60f52ced09e50dd8401198fd48ad6741f545fa1b53ddd0b55e71baa55112e01f42731148ad13f696b63e307f3f9878c74bf513fb03b3bbcedd2234f8d299286add8e0f7aa4c7511b9cd080e293b0067b4bed86869a814fe4b2a5b5f2a9eb543795c7139211a61de2d5dad8c61969a0a460552589867d544b3561b8234e7d16ef4fe6590d141e3b29a77e967caaa25bfd1eed4537e5d57372e621f4f99825f99ff7016193038ad5f975fa64497d20f2d05ba4fd2d015ce6ae8d65d536584157d0a4269f5421c308818415235d15c2063bdd645329e270c4c6e180575ba5b20e98812520a41d415df1b7acbaa29d6600b797160d9e4b7f96cffbadc623456aaf9aeaad3fbdfebaed9b7725f4c5027d7acbdf36c3aa77ba6d2f834b5b45b2e7f1538cfc109a4817c913846a4d5a479d5f29e9b377144bb76265d1747644dda7465b498b3ebc4d361ac8817071b60ee23d0803d19a9da55619b430997ebc41c59d8feda62b9e1f287634b1b8b22f415c89444c01af938c50e0fc828996a044d5dc514479fde095d6e91c2e75e74af4dadd159e4e84d9dfb8e6e4e3fffd7f9a1dfebeb14816857dd1b6e197fe33d03f1835f91d2764d3cf8eea43ed03e9d4486cf3dc90f34a4f4e7f1e6b62dec4936284054322904ec54c8e3d618b4c8fea8c5985624b3625b01a904512da4cf3a5bb5d23d99fc6f72f636e08c23d435de96b0d52f16b68932e2a03b0ae8de987e2b9a324c3c1028236ffbbc85399dd8e441ce740ab549ed1547591469251aebad65b00fd4d60abf9e3072213e3ab5ecfa9ea0c1dd98141b6f58d244a6979af521e1d1c04b2de3db33a15b7e4845046a4f749b9a08fac86c32b958079a48a13743777aa000581a58151b3bd18f5b397a786d3323bce206a91013fa451a2e8c0c56cd428497e959b728e88a9707a80230850f82388c5f653adcedfee27ef1d045703dfa12952f9ab8859c610f3a9eddcfa6f3c470b7ec1cad5c06f69a65fa5e8641eff14a5378fdb63099cffdecb4816f5fe8fe64f715b432301523d2280be3e3bb2ff3374c5fe07f1342a37fef2b46a2726d308f9e678246448528a219ddde24baf73cbf4459791f564d9fc06b22635a0580b3ff21c9d5f6ec4433fac3eaddb055714aaceb48ac87af41f00fe3482a9888afa7a0811cbecbe64b264197ae22c1e17baae62116e79948771731bce85068dfa4016f3978e39ddb6373b398939642824f763dbbfdc002af17846352ea59cffbb3b07fd526936f313a9f36c818b9c8935d3535e05a7f8ad66541a202b3770e7b5b9ee000fc03f87a67ec1668c270bb6dbb46e1f58e999887cdaf5bfd08a899afbd1b1712a474fe6bdc0e3c6540072ede5b4fd2eb4da9b93bbda506a634eae7f9b0855c40609011911e765c5bf92c7ef9876d3898d1b9f74ae44dd10de3cc31b5056f219ccb522f6c48d8e32893118f6fdef1441251f5f1ec351b2ee7b95a2ae8d08edc4ce2d5749b2f07321ea546805fc4d46c27392fb66ec9ee04c41c8f77d4920dcbe5937cb5507795210b0acb300b3a9625fc4edb076cfd36c5f8f0dd519025af2eaccbc13747e1c7a848a5deafa9caddd682356e21d7d24d7b3458f8c2351c20a864a152fffa4d049b331de77b2de9490bbede98488158108571cb80c5e3eff895ba2a31a1d70aa1bc3b38cdf5610495f432a9f9369b9ceacbf45da63cf533e42df66df7c7e108146bd107fd890e32252e5e1125db9e5e938fc86a3ad8547951fcaacb69789e99bff56fb9e0350ec742665cae725faba2dd08de44a16d5779fd5c0a3624db0bd265d933494020efe58b64b6da26b0a78377fe8d55a2576e245791efbd9c2811c66374f1d95121ba4ee1b85b637fcbad348cc8b089936520a1c19391ec9186429689919c0692b665e95450bb37c8e88306f1d367cdde245b2174277da0cd799788d6ee9ae8453adcfccc3a5dc9832cdc59946ffbce28be4a4c0630691f5e9bb4f9277df568182fe478a4eac8b6576909c6780a7bcb0ce8f7d60f89e2d76d1fbff45d6390c1ac743129754aa2b7320101e0e7da7327d6c5e545feb5a0c48c6cacaf245503f87d7b7ad7ddb24522df40f94cea9a9044ee7de7216d533e16823ee8599181975253e5841c4a88f71b04d9465eee909c6b1e220d11da5bcfd01ab009ddda154828467adfa4c49cde3991a2062e10e74da1a6535ced9fed0b6d9f4c4f507d711d8879b883c43e7c3588dfc02a3218925feabe7dfc619d110bd5455cb79ad9cea0cbb5d2220c5d83c9a2169af546b260c4eb936a74db8d1538470c73fba41010963314b1ab662230d2bf126102eca5a8e02cf3fc9c3e406110d44c483b5a1770c0c97657b251ebfc59d2db5eac1cb2715aa3c5b65c1f4eae95b700bc4815b663b61d0cc7b5bc2b8270d7cd558edfbe3086cac8eff197ed25b4ca98546735f433db5ccf9d0b01f56400f9eae1656bfdaf81137c306211011e09b15dfbae35c8df774a3bba8e40cd3d4fb00a6880e445324f26d61a612acbdd0737cae1179bf26bc29b7cb0b23a907ea011402947c0cebd8aaa82620a36ab9b6d281470d6f05a42c99b87981f7c45adcd01b2b47026eac614e0930976ba5b0ca56e81fc5277d80a9c7ab49d670ed03dd760af98a47e495a3fe92c0f97c4138958044faf55c5f4ab7833f1db1245522924609028ce1c234bae58d99e377eb82856c7a8fbfc6a21f3f2c21001badd0aab473cfdecd9a89d230ff477d43739debadf3c7d03de3987d4715ac54ccfbc271ef5796428faee43cb46feea3a73ca2acd73685d6be66e86a958a8f29ddf8a7d3f9f444b399dfab5a6e46aff3d3ae8d25a4c737e4c2bd1660b0302d2381ee4dc9980fe75e39da89af19310d61a4042f56d22fa2eab5499eb2865197ef07226c05600fd904f67feabb07a7b6f5fcd31302755a19c28f3fd8fb34849afc27a79c226e91645642229a0415ecb6279770561ea342e043baf8907a6e6a31914bfd8295ba6926bf4cfa11a369e10895d5d4ced4c426b69f6084a304804083020e812d01b0b6e6591dea9145ae40fe4fc0e820c2a3dc813bd6b1e960adb54628f1fc5b66df7e33cc9cfefcefaf14a510ecc1ee5a7bd5656a34b14d3bbd6e20034b421233e24d7d8d80b51aab12a1d55408e3679bb3c68997729b2488594ba5d8ac0deab6b93fc38ee6db89eaa263d34822bc71ca45234db23ec5a6826540781358a9f9f360b863204e81d07046a093dcb0c9bc3c0415b7c45722eee2ac88627823b78421d11f0be2d32590c2380f32d355229e34e33e44ea3ad47be9021aa521b3832b8edde4a37368ee10cc09141822d5dba7c5b50705b4af3e94e3f9ca32840146fd4f5f6910de5004fd23c3089556a0fa92e2645b9a3b9f4ac9e850a3d3e7b5b8d32fc8bd9ebd30d9d5230b04c9f21073dec23366748560c6fc67d741a822c54ebe373e676e515959056a805f09852a111a6beedec8c1ed6d8133e24d04fafeab7b877b71bd107db03e7a47219111a98d8967405396fa2a12e30e5c02b0235d6ab4c414d14393e6bafe123ba93f1c24fa303313ecd123af3c816a898e8c015c977550012256d83c8f96da6129de1b2bd857afee09a2122c264f5535ee17207e07250e756051b665c5130945b98cba09141f4175a1dbfc6fc3440f6a2692f0141a980b856131e75a7f0072305c0065b5b0b32d0a8a406f136a67d8bb51974a46cc1fcaebeede69c8f76e9cddc5c040ef380555ade3492dde6f9caa0d4c18d02106389c3334dd85e21e5d1198bd95e32a97fcb59c85de999cb078d9d2982408cae15ae6fe9fa95b4b5443e8d6effcf4b3e903f021f42644d1605bd52861a520f413bcf9c5fba40195bd534ced9088357fc3155c7606ec5b857eed52106d4472dfd4220c46c88a2e444b0f0ffee688cd472ff90304853bacbe594e7e9eec897b6f6fb0757a658f76ac8a36f995f228db232ff365c842a95ce4dedeccc7d6adcc66f9723b413de9f410bb3a62b338b3bae5e9115fa66d581a5d459ec0bcb09a013e63303e89073cb8473896915f1593c9a62c060a64c8d164cae46388ce6751a7d11e1246af906e1fac401016bc7b8ba49be563af3fe509c1efc2771bb2fab8827c60cb79de9a1ad7485cbf6f145c4117a93b2f476300c403a1fbc48af19666556697857a6422675ee39868e4d694b538a5d90a741829ea1eb7f16a4cb74dd92eb71cfa84b5ed5eef1f5a52232b53d8791b48fb98cb0bf41a881f11d0d202a40d5031ac03961c345750b8d0846f28f76e663b931c0aed8b5dab097d144826ebe16b8e4362868b4b105ae28e4c458982b0861c0c5ee362ca4a714dff9ce082629e21c612546e4cc50fc78dec5ff9bb6cfb7477499e90b3d799fcd26bc460448ae038f4d7dfae99e1ff63d34285a15592955e50691548ec56edf4493f54e90abe856c933b365953c683767622c31bd2b18c50eab5e1e8ae29cba3dbad2199a4fedeac65f4fcb0094524ef23a1d0001147cf902c092e82eb5e2b80340ca64adda9d479af18673875eb661b3f085a016b7fd21bee7789755ddb38555338d36bf6277947fc8debe80d3ca65934882e6b03ae5426dcbfb40efe0de27d5dd33ffe91d8151327b8d49232495a021c7d52b1a70810d270f473b9c0a9481744b0b51ba5a3b7f1f8f86c2f23da41f61c2bb60b545bafb0f7294aa9867e2d1c2c5a1b4bcad31373e8fcd29118f455d0334c9db169a438fc9ef021aac8d8efcedd2fa51750706d4a4fb95d7bb5d91e86d6b8b10b87bd82aaa18c2074e54f9ebfd46c1659a988015c86b7ce187b83fa89e27ba3e91b6e1c1bad902723f0f1a05a9d016e0e6b587ac9b3f1c9985d1ab1975a5427bf34b812e87130b44812cd2e4c5dffc0752c13c523bfc86baaf12a256141f12f33a727abe44c948d58aa76f91df64f063c36b9a6d04dcdf7201dd7c3ffa1d45eaee5ed72b85dc589bf8147ef5fef4ae4b949c2f9add64cc0b45b05723ee3701165b4a5eb8a442dc0af576512b0272338702afaaf13c3fdcaf4854a010733c4c56d1abfabb061cc40b0dab4b6c5861fffe5e3b8294d393a368c38196036b257f70d227b32140708516a1b97c9c50ddba5ab246eaacb193129cff1a51ba69b4744b712ad3477c6fe36a940b6aa5ced1615fd0f9e25ae89812eb682b8a7a376c1e8d08e589cd9e3d29b25ac51e3e8e3ce7d4b3f8b7fe1ac105c7ee26e3f467cddfd77a5da729943a14ce537cf7f54170293996834a4206521a03da2e14bf28ff11ec8288912d13a08631e01278eb5fd631c7ed1f2a2c72f22f7d7b96cb27c27da9ee304f70753287d4e288e53b2781b8dc405379154da26904290850422ff4b396a1352ebe08c7136efe738fe6a38d39b4275127b9f230f437cea7df223b3b31bbd88aeaae1f7ab5e93d39b7f4efc5f2bcb6fafa8ab485f9c121cf9526fca23f3213c5ac138190c9458236557618eb79c0227678f9fa5557a9c214530bf6b858cdb46cb259092425ce63de420298a7ec7ee62b8b5eb61a3df4a479b68e0ccf7ae1cf4908c0b5ff2f34383fbf155c94288614ae02b8f1c4d30fe332ac7a87d45bf55c9fe2fd593d6160b92795ce24cebac2ee6c2803087515b8d44c9a5953bcef88124af2e82f01e66a2ed8e3d68260ab9dfa887036954d3bb9d9b43860bd482a5c8ef96aac05909de00e73d96063256dc875c08327725ae2c71259001b49acb348cc3e2db38372a67cb2fbcb136be5597a6207e801b08430bb5c33e9d1880621a7508960cd3df0c36ccfc9d0bf417fb43239a72490b1d536beb736ee74d37e4745d15b1a860c7c43532c51621414ea153daf68fb09f0eeb8915f0e973013dffdba60c8d7bc807651ec4227c0aa4fab2b04001fa8d27b3743a75e91579551d1c45d1b47994783e5b9b79eba1245f2c779e856343fb34b15b9ef2bc2fc7db25df53f0880184b6afbd88813ca38930d58c6bc371e1e0c6729baff00def2818eaa51ba356d6baddab556c59f198695d58621ca609d187d9937bd4dbaf0a55dfe855d2c6f42df4906eec6565c283684b51f36d642cfc88841292b899f4ffbe6c7bbed34db11ba1646de299d752cfaf3ffb98c163ba8affa22b4b96221991b48a9e51dd5135042e2e90298088da604b8cf7756f6c9904a55ab157f508a028de1cfb65e81ea8065f20b7ed44c15a8687351bdf200aceeadd5542e9e33dd85079a46b54ee44712d8cdf2b7a672d1607171a9c00221cb04c256f0a355d7c98cecc9035ad45032fa6a053333e419250f4b57a026d72b341fd90c4ee43808c70569058a7f391962f39158d84d0c684fa4213551d76fca21d56836a64eb638b1a1b8b9f9d22b2bcaf3bafd6d4ac633dff5556f622ee96f4512aade6f336de2ceafa3efe78d20a4e5a66549a503a44fcc91704b5e865f451fafd8ecddda1d09315f2f2938df845c1f4bbc55615f1d3fff74cd263b6a60b5ca1ed8fdc3e1d69f792ed688d95cd679873ce958458990f2aff5dc86292744cb698d93a37da64965572f663cd4b2203a80685bfb55936bba99b9a6c2e0d6cad1e84b6b62632110dce3b4995f481cdb308b9b9c13f2cee0c92f552b148eb2493c63ff3c8520c4b6660522f7d314f6e5615b81e97c4febd1ddd66df9617814b0f84324d683dbfd082d803ae2e6fbd76ef66fd9a24f1e63c3f3335a2f61554f8be4dd0cc888afa85645372da0ee6d6730d32e5b5c7637564e7590d73f3a86d5a7cfc3fb0079faec67f8f6891d70936993d0b3dd6a385c98b45830b3ec76864347b3fbacc90779cc5fde43af74b5c327502fc6e2f0c4e0533d4d30aeca71273a54aeb815699d38781fdb6871992591104a2617fe6dfb5c628d95d1f6caa29194305f5fe15cfc76d1d18509fba3a09c208aada3edb8fbfadcec634f30866727260bdbd3bfd751700bf9f78a3e896ad23db343877e7ca7b8f0af80cd97ce764ef93c8f5eeae86cbfc196c4137bd0d5134888ec2247e62297928ffe4105c1e40d865264d1d046ae2e785dc369daa550c4861ef870913916694bd0baf4257662b413356ea72cfba8872eb522c6e1f7db5604916d6ecf9d4c8f74999f45af1b486a886c8cf6cc7b8cb8de8b6f9a7e1a6d445a1a0cd69cc3047f4a49a78cb9f0af584d2508c1708c912de3e9567c2d93a5dfa595199aeea8e9600e9193bec7f61b7bc5032b59c653f654b1a2385b5f11266ae2b7cc9ee7154a2ec5b82be245f37aa82b7af41d11e827f965f7819937d2cfafb34e2d67f8afcd63a49c68c2882937378e7d5b1d89bb72f4eb341ba6feac61fab677d42feb8899f763db4c1dba15014c852edeae1ebb9218b36fc465a4f29d6bad08821f7838777559953dfb9b80a164de1f0b757f193be8a8572231c799bffa981a89afde411e7789a567a462d7d0fcd351695f09bbbc46fd96fe4b875135110c740062992867616e920a078148dee0d87a47161c84de9b4061480cf6c184bdfd8b2dfc0d668287d7206b52ea84abe8692c9bda5318fddcc59555cc856fa88795cf981f987fe6a2ec5382bc9bfd9e5c61a9bd29d6c30b72509911980ed85e5f1a2baf1d630b59ed80eae8b7bb8811c1fb43115bf0255a5608345e00a401b82938c5def0a553b921d4a33998dab73aef8dcc94a84629876be7ca1222bb03623c2a684f95acd7cf0829ecd1cb1323a3c2837476b9563f9d3deef4898bc50b48d062eb55f77fba9b929805a612b004e4d67ed7dc3db2de2ff23e696b525334e3645015e3a5926ba734e3a615c037d5795d180fe269ede800d6d505c92b08a35331e49d8e91fd41fadad67480489a1fc165ff593700bab7b98daaf6f47dae85c36e267e8517f7992517da7f35c4a741128b08484d34d9468fe66977bfd06500e286cf246d65a8121be3a6392ea1c011f110882c2c2b6ea8ac13858864fb1d8ec8d3b00238bdf7f17b0e1719b13642424337a35e188503dc4a5439876e201158a082c2035e2c39cac23abf0966a44c659bff7580e063fc565db8d0b319afc7e16062b3bf5f840cbaa07b589d0a68dfcfe75b78d67b2a76e97dcd72a5450dad5ee69b61e4e7717afe68e92d03f0d992a3568590d028d1b4382644b383756541dd7562216671b936ba8fcb01243f03ed4b540fd2a3ab2834583419899b72f5912b4a77e39b638db313eac03b17de82f4576f1578762b83948a7f86ec118a1cfb545d3bbeb33b9f54e2171b880fa9f4a312b721cea7d58b8626cf3ec13c1f1e9d0ad59682400d18beb96b512e5f6d5564926565afe7586162180f6dffbce79600eeb81cfc7465c5820d40993e3b1cc69cb79ee6342dde0c93b89a4b0b57e907ecbcef47f5332fc0fe265d7f4a813cde04f2e32f24b541f6d0f28ba53d632a9317bca045afb660ff41986b421ec5693a0b8c78279f636118e618f192330ecf34f533d3af8f6529bcf5919cb2d4222e45b546ad303f27f655f4d93a867fd929cba132a460b7063146633826809f051392d0c9362408c6e21182108e72538a04b6bf8c80745b3d1446e15b80c26cd25fca5a2f06e2afa0b73041d5e020d8114aed6dcfa8f3692aa08fae2cdc0fa49630bc4520b1c70dc4ff22b9ab64d589a1c8b9d9b15a87c8e1514f7f328db92b0ff5b1b56a95c136b0302697309bc92a0dde9f69db9527ee1d5b42aa2ae05d31d5f84ae266219c8ad8de036bec43d3b65b326645430abf28f3a578167c2b2abe0f20eaad0cc78735bfc0159598070ec2ee2a448a52492bedc6f5743cdaac00a5e0b493a82f16ded5a7760b4ba7db565a96aaa2629caed538d23999286b0395a59b6a32419413b3e3ef5e52c504faae2f54f3801389ff2aba485af2c46cd1218d94814d46dc6733590be1e559da9e71f45a60faffa607d39b790a4c14545b18f6aa9f38f9149451273ddf464501dfd67a55c0f95b9bcb7d378d523c52d80b1066ef5e51baa34bc5575589586ff31c8beec4473cf78072ad7bbb3191abc15c701d3d8f390e91dd0d99d02dcc7129358895ef6b31544fa071e56b8f9bbd74e023b6358f4c7452bc6c22400029f53fff4819f986c287fc4f26c8f071565faa3e1da2b6438745512949996c88b54e39c0dc72f389f7aac0e69158e10906f9ad41db237c507b8e7b0f24f1815c23ed0a9b2da7618196b0b6f50bf8a9b941112b2264709ed2797791d63ac0ea0257aa37a8e3353a2dfb0e90000ea384459a465b6cff2f0671046bac98042c3b9ea9f4432808c6354ab0adf0063448e7d9cc9bdc53963d183411dbfb7209b4ece4b6250a5861364347b4f4bad8de949d5413dd4651071db6eb77c6fa730d312754f3541ccd81c29f24464cc4d370fc6504e6710902538eb75fbad327d6e74c90b2b346dfeb9234522f5e4735259222088b92e1402b4b9e9a2d5ddbd6aab5e47c8abd3f7905d52a832d701e44f8c03b85410206cb5432fdc6b925182f119663f312ec175806c6ade1fa07ae9a43cfa2f7495775ca947d66c3faa0a087620f78cbeaff99955f762e8d82a2ec20c2a5282a87f716c96a5d6a5de2043713e197399bfcaafdaa75d5579ec6514708e60cd6c7a52b9ff57efbe70ab973a5a5ddb7e23cbf19906de5ece03fd1b7c58436ae574bb51b467ac65a0b5684c4d625a3776b10590fac1a9ce8fbabac4bbc369f58492a4734bc57644fb4ff3bdf090fd4db40afb5fd39d8419b3db20b090e766ef4d24fd28a698e12bd94c1961a6d0d26404865e920b5a08dcbb9dda04391b4bb9c4262522139195ace73343bcd2ab056167ed0ff0b98299dec424970d87d325a2bf52cd3ad2641e3459996dcfa0003dd17de89f1c457dac80e3beed0ce574491848762e1163e6d3fc7e876f0209fe6cd101e45b99f7943cab69f6186d9bf6d8e520d7539763f5aa2fb4e43491e6c2142608dbed4c91fc1db473b3576b8e3c1fff7432112e99988ca7a6831a9242eb3b411f03d215e1748b4d15d8188a7e631acc38d59c3d144c1335816a22e0cb5ba99d7be75310a893bb59dff8fc1689c64088723aebe4da7da792ac20e8ff5d4dad89a5ad69f29f596c73bc3a4313eb3bc25312e255f4c895ace26cca38bdf7afa9698ad4a410dadfa286aeb8d5f10c467b6df332127c682b989308b9ac6d77866cf9c9cab9c7148338a4ce3b9e39ebc4d7e1dd5abcd5ecdbb25dcd41a74eb40afa638f6ee1ebbaa31598d5e533147dbed0f4121b9759f32444935800ff21b23f1a4f53daf22d45e680637bc82eaff186540ffbd82831965377fc5374ecc057fab5b606d45df49d1e46b7ea15c42b77740348d3deba6eb02784f09443e251be7beec1728db61c8e6b7a46d9f61c25854c8cc8262afd14a20a37de4cd8bfec05d5a3d603b3f0665c5fa21be03054cd70773fe92364fbc2776b591a279fe000bc52ff0de9b5995ffefa0ab11324ecb4d7848761bf855fd8149a896c9ba8263cc27a90ebcd2f9480cba5ff5d4726cacbbb55562692c74645a2df6e0c965bcc907a7a243e5e6699af637fb252ad071c47e2d5f5538c4d4301c9c176bff0abea559b6c42bf1d0ce04873b99350605bad00e0577967414820a8e5f5b2316d868c9dd5cfd40b2ef35ca7bfec53aeb32a94dd9341b8d629200518776874aae6c8e3281196458645fd97f223da99034b4b3324bcd1a4c51538d363eb2f3b46713831f58fc5411939b6bfde6e0cdc72417090fb4e0905831ba58fd8ea4ea0b9039757e5c83bf635977770bdc802554b54ecdca275dbd921aeeefc84c934f19960f7125a926ef85e30f87b045c2faa23639860a5611d3b4b88deb464d8ae37d53c0125892e226024fb7b1282819f282531dd36ba9f1d6abdba037acf1f43a3fc65f793a858991289a2d00417f8b09fec15c7c0a3d863e583069b93a5b0789150ecaa8be5d54cb88625487eda5dc5996a95c2e15a46fe4ac3ffaee66c0cdb16bbc7ef90894bc427aedc5beb2144c02f6fa9ed8d42a51229eabab54ba7792dadbc0f11b73c18fe5a15610f4530e54610104d7b8ccc14108b8fc4a19a0573250200b070f41a8e5fea8826f0173d8965adcbe90d3810e2e35e9ab038f2631bbcc18af94177a0f1c0af02566971af4ffd6f5bff94a3d5548f9fd9ed9bdee151a4a3f6202a9271fcb4635dd915e0a512df7ca7d60047cb6dc5c128df391875ef3b09701ce7165c9798f2604fd25eb078f2992ce671cadd4313054d80462090290e1a62164dc5ac96f45c0f12654c797f6269a37b12ec4e3b1e5e4d9e5dd17f5262ab1a7ebb873f035aa268ef9e374d6521aae6d7af2d6d65d836e79d6461c5aafe2c91ec35374a038b9ec89748c1693122ac6028d391d6f5143b74b842f53493d5bf6956d26a33c63c9b10994034a36001ddde26e5b8b6c1b8ee76bb94748b1fbe8d2e08fcfc31e2fc459f518282a609c1f97296185ae280c4318137bc411b8d097c40af0a11c2685e1ea3b37f425bb4c78766a235d219531d2cacfc12404ce3de0ddd4712f84671acae9bb139816bbcffedc1a6c273f4d86457af7aff6132e94cb4165e90b16491df1e67ffc0fca1c227a10be9c46447cfd0005a0153fb3b535858ee2cce73ec76ee4aecb5b73cfe4565a1a0ed1f5d582b1e7d244a413e93f5ada361a58443fbe0edc99c1ee3368c7583b70c21bab896491c86aea126b6779294ad1b36fa07ea2b780d4a19c5b0ecef284d444537bbe741c4cfd6987ce20f6c6765ee90d38fc7f2d587d3a40cfd79f312ac44ac734adcd418741d2bb793a468ae43c854ba982e9f5667a488e40b4aeba0eaf81e17b0fd0c76a3326591ce94be7290f30bb23307dc78d6b69975455d6cd925b09bfcbb43a591f9b509508138f6f04aa7680111ea6a280195cf3af9ca06958cc7954db0c53545f473207520acf19153c1219c25b14bd52d9e8ef2efe8fd44716de55f4f3316970ce80076bbc4ca37b93ae358389c5b72f0eaaabd6b500ce63e26fea094dc76e4e7efc0adac93388f5b501f572b9b482d6586a9f9a678fd5f90491eb94dc7359bcb27308b74bfd4541e13c76c29ed6b425beb4a2b68ceeb49503fc4226e980d0fdc1dcaa97f962b2dae3fa4628e1161be82d56b87234ed079daf0cdb95b8eff084f5dc693ab6bc906befb0323eac3974cea284647cf84c3398d1289a4c798daf996d8a1a77b0e326c8241fdc35aae4112f0d306691b533fea134585d93f86239d4aec0907c0476779b9f0255f7ba0c48463c0728904f3f49da65745ba82c6d43a1f69fe0a3939a9dbd0a1a326c8f122467f7a474a1f34db3eae662385e671c45d9d764ee7480f9951044ba58d7202bc749b80f7560df0636cb567dde48d4a40e05b06a02492da43408c7eee67e830692bd56dfa83f2a83925fab3341fd88abc0bc5c912ffd3e989a4809410bc7a88a586a81bf6b7790acd86b4afb10c88cbadcde886424d5bea51773cdd5e3e5cd4b48e61f50fff2d9633fb0ab1dd76df3de572066b9659b18c94c682993a1b63577a8591032e346201c08c821c1515ca4521bdbaf1be924092eadeeb685fec9b2e94e5e7a8129068000c508d07d1059ecb2c44cbc0b5c35e209d04ebde92d82388a6a40bfa63e6d9e185c09857aa0d56047e118771a7801f72d31d91b2a9213d9211a49b49273ce519ac51236030d485f42f08ac720bc1c29c97648784d183cd4cba06b51f55eff0bed608b9a9c05f21e9c6243ef9f294fb23b41028bb9e41fc3e9564916659b5f890ef7a3e49f22981e008a83210566c9a1b6d29a341a12d947601bbbe20e8a2ff2f4bfbb0b7856b4a330eeea7e24643a853884ffeaa0c104d27a4b31f9082506eccfa428d821731e2e62f7af51165e0665b07e26048e6fcbe046bded2bf5d295b29d3b59f119d4b16a8aaa926fa044a9c4caf678041708cf383e5c494f9285471a32e0ec1eb9d363305d9b6645f719d6209d1a7cbb8facb0e40f305ba618c4d36a07289a0e44c957ae861fca29399d576c3a35c1e587381078a7b1ef99155dfd7f981d352b88ccb039d344f5d04f0cec3e14906aa1fed6c5a05853ea70c1dd06aa50266b8f687478e1e2e68a029024fa4a8072a586e336f9b47b947d7594c284a42daa1b2da26091c61f80cb82f7f22b3aaeff341999d1b8af2dc496bb674de530041925e34b039a70a5bd4df33e99cafed534296c8bfc593cd48eaa757e57fb8483e42d98335571272fd84eb0bdd4dc77e4e78d631b7b61c769809bbaba76bc6d5f38884569b2fa542b71054a3f4e5ae1808e8022c1f02024c6144a890b9c37aca0905745d2cd0ac292cf0f91dc20e81dea89a8be98bdde3e2898c02e52fcf8d6193b9a3c7ce2b311a4e764e3980a902f016a1a4fb41267ddc2966f82b8324f967f0b2a3cdca7835491360c23deeae74171489e78ca496d04235576266b834e5971b9fcf2c2cba6bd4803caa4675d6efe7811a90233012d1078c5f7f037e403770ab5d7e4f3171f299547cc45ad7a02c453314f5f2cc40519895f0b4d9bd6bfb70d6a69609979435c41d7dc816d9a8ef9b1ae1ac7f27a281cc93ab680cb6ccb9bef638381308afb37be762f2da5229025f2746abc1dd54732bd8b831994c669ec07b3110ec7cbc00753ed4e40fd0c7284842092aa1636963cad046c97ebc5dd3d732dc94a30b3c3ebcfe9bdf40e550cbd1d229b1907d49832d5d321903db8037743da1ac98e6357f8b65d1add1c2c37573c5ee8843fdac1a938565609f3bd5a76c92bb8dd1418fa12f74048bf1495007a60b1f015a469bbc47bfabd1fe65e6c67459a6a1aa218b747b52c3bf8b51b582332f2004875c7a0357a87b0a04ad01678b107f9a2b9f3039801e7d204462df86096aef837f6d71f9ab6a08d17e91c05de652507879b27e7dc6870c2618b4227aa50f2c3efb8c1568610e179bf578c13a162e54a59cff0e08157198b56957a89e421e9dfba696b3f42ed0b9ebf0cc70608463cc4c376120087bfae5900a5d3c9ddb097aad563b5af2f6abd112323b349d89702c313b0e4002df7a3664c188821c8c7f0571007570c6215c9bd603c53435a77da0b228642d30eb6e84eeb3d0e4564764544edafa987a78def852d72bf838b244a29c1065bf24afdf69f683ea11c7127efd9635a1aa05cc6970f9d312287950a6c4d2117cc2ae844642ffe1d2b004cdf93f2da62081f3957be5995dcd97b590a997b9a093f64db2f861659dbc80c8587044db8247ed16db8eda9bc32fe068d20790725412c66ab95ea879a373a607e9cdb5a62a693c01f7a0a4031ad8de040d28ca5448cc9982fe7304cdf6c6deb9b4782b3cdff0f8cdc00ef05bf3c6ed3258eb90b01e6ab32652c35f8133223755d7ebd73c24989e225aaa168afcde29289b4771596e885005b3d6d65c73a5509202b68f96fedfec2121326488ef60b101e8e60d149d69ede2b08782be1c7aaa9e0c5255dfcab36e5459a25d8f447f868ebfc9ce939471fc807b77eb5a00c653331872c774fb68c212c343a8bd3fc9bc52f2ecbbde2fb2ea9605c0fe6a7dc02b5cdd0f84974cade1832939d2dc0f383626e1bcc3d9193d96d8fec338617ae0c5ad75466d9050a4a7730dd61a87ca62a2143926d7ae8cef2333268d2274155e297d499f7730685c2fd9c863b97c7a3dd01fc18fc3bf42354fe6246d6b78987dcfbd06269081f8975c515d351b1cb8cbfa6c43925d51666a279ce13bc79b6136f195cddc998a18d1f0ad5c9997dbf8c0643280a775a829e7138b5051207e1b2e57f680f82cb3a3484bdf7626dd3267bde824c3ee7995f6f8cff35e71de6c612a06dbe344fc8745adee861dd7959c2b5b3aed5fbc22e65bba308932dc5ee90071ad7b920c5698683fbe7de1e980303df957881b54f1b750654ea3f00aad73efd82b6084dbc646f38b2ac19269d213a0444584e7bd49a30f011f5a2f6bc158c4ffb52aba4e34b212c01368e2ceb565df8cae2dd4b2e1a30cd06e019807104d08238b53ad30e231c4f8d1440e14c21c842ff709648e78b3ea80a29f726113a19918f1f74e39f5052d63d98e66005341688f57ec438b7c6cb90d21a2a86e695ffd48f242e6f642b55194a40fbafd0198f8914c6fe5930fd152d0286ae1bd23848e27fe3446836b2d76b960caa119421fe7f4e7df092e00a7019cd58301882e878600a3970c970c33118d43d13c33af57ddb66085dbf02a9ef4d09b5acd5c6e9eb48fa140713b7987ede4fff7ee835263d99db9df706abad165ff5c56debeb5791cd5b0bb1e0477d139c24d66944093d2a5743fd72b8960b4d602836c536a942ca45a2fbd29ab9d8bd7ae7d65e64f292f26fc96d7088909420db9b303ac963c427a4d05681c4006afb48aac617368da5661c5278a30cfa002268cb3195505b2ace8c8cae1f1fd5b34186798f56ecababaf0b790781e04d08eddd1da8af98950ced6dfee3caa49a78b16ca7049ae0e378da7c9ccd216a06f98cec591879d8ef1ce2ea2d1fd01f55eb92f606a70baf40c999583829a895d74645255202e2daaa498c77f193c06e563a9b3561db8983448cd6d0c9d06a148ceb58082e362864290f38139c148c3d802a3aff13a2e9b9cd5486d89f68478ad22eb6c68f199726e4788cfc2d3ef9d4ede892a99989b5d06b31bc9b76b9374e8ba0b46723ca16bc42097b1666cb762af7d04d78547d7cc036bc211a3ac9b1ea706a2946730b67b50a34fcd135bde01515004b70a7c5c7b588bddb0984b2ba516add041585d78328cf8bc39d081727dd794b7460002fe136f0f2c4005d70a0ea40f11a74857ea18b78dc79a04552d7929b1a4a279aa6ff8cfaf1dbed8c2417f4f86adb54707366df2185f97e5b58ef83bbfa513f0dc53da5cda1b83d8449e3c7868b9a194f962a99b567743ed416fc4eeb390b729ce72db98b7e37690a4ef0578aad7d3975d57e4c982f74e2f1c7902096d2fb31f58512acbfbd480dce54b1d7b5ea1b2493d610f03d3bff2153dbf2a2a4887eb6bd335192cfaa6c3f48a6a83044dc24ca5c3324197bb0ef6c126e4ef9129b6113758c075a0e9afca077e8c92940836b71c1ab34f35a4345b96f437d735be8aef40f202f281e6bc178c65b38aa65da2e8ca183d14aef4c86cc88cb6d506882cbde320ae5a77547f52ef997c3a40f173464e1060380e5a4900e29d081cc34191945c1a7dc69a40e55ede68636c6673752e3adf9e5a217384dd240af5d1cf28b37c7559b964d41ecbdb6e70b9f209fe818c9cbee404298e4863ce600dab3716abf797ad951fa831df1a85a3752e31ec7a1cc1115732e7d537990ba7ac238d14862dc7c256d21e1af90009479a93c28c86d3077931118be2d5bd79640187ac12722e0da3e0582e86efa6e2f5f7b463049311fa0af24b043167b4ac57cc6d0e4058d5e46d7533be851c397c646fa0cde2220235c305ca8729affb67ef3dc3c5a7a1e0dc58e2cc5ed92e4da36072cd55244ea5a2ca061ffb1ecc83b0953fc527c2321acc14df92f9f650c581152315728da425b6e117a3f089897482a5394d363a9943e1160bf113652a510381e9c194bf52fb1986a098e17d8b8594a923b2f7beaa095ecb50ae11d94591f4f4e2332dd85695e588d1fa66611c7cc47202605027fb2512f587355f78e193332ff6b98af8b48a30ed56b498c2676e06921ec6bd322d00064e5d2417fe5f7af871773a80ce8234b5a2b178be151bc79776191bab61a480874edd3740945fc8da656cbfd22d5bae2ca28cedc420876445dc02d3d5d987e7389a972ae7a8aad24c77e97bda20aee0ef68b1014afc1533422728108eb6c21417784ff97bf88493404aa3d485d4d738ccbf5f99188b4723b13739ca1b158341cc62a222e1e5e7fa225ad60dd8152ae44975642622aa4ed6c5615bd309d4e2c793dcfe5a089b58045307fef21892b76244db2e788693899e21348de1c7262ad54a6e9acc2308b71457b725507f175da4497be1b835d3f483eff576d8ec13aa3c57b7b11a0afd8a477b270813eea4600f6c919162e2aa2764f13335b917e13e85f71d4b5aa672389caba88cb547cc81e7cc8c31e88a64038534027af918723e9ae5e7ec32c5b68b8fe6a803b19775648faaeba5d36e7486b9c68041485e8598701748201b31e8245bddb180b17e2230954cef73d3815afabf991dfab9071fadbdfb2b153f75ce519f8f7b479e752790c64d18f32ec7ca29359c994a75206a5c23d5f341d784c1dd6bb6df3caa4ec39dee34e8b65dab6cf22e599bd08b72f686a91807284a7976ca613b5f3969ae4236027b0cc2f34018bb0485bf2222b5ee8cd9b74d60b9e4923e151e29f208268ca9b22514e8e2f8cdd1f1957d68056def464f63fb35836688bb3c444afea76c355639f3b43c0f14832b5313892bcd073da802b1c58433aff0d6d8712e375f6f9c6defc9a6d7d26e66d066809924ab0fc3ad227af43acd3f67fb9a2aab0c2a0a6399154bc988a3fe4983e841b9c79d89ad26c6a3abf912b43c16c58e23d84798856d5e802c4fadaabd41b7084d2d20a7e8a775b5b41f27f9fa7318129957b4e3b6261812c0db74f8ab21f9a810f2c2f979147c31d310b502893ea9bf3e62ad2bd6b088ae4cd3d268883918ff230a958cfb6009fde57defa7745d156795063b92a27aa598efd559db04d336414fcf059876b389ae2a9843e91f22b181dfb2c31f76fd3acdd15d3050ea7e91d4e2978899d0cb53689f687f2a2eef9191e55de8d397b21f9dcbabc170b5cf930b192780b07cbe27720dc2cb66773dd467b7715cb278beb1e3c7392fa3801248a1e848ae3136a371a172d6db3c5e4221492b011c336c47654d0923448d1905661944d58d83fcf968e6b7e09d28f957f6a7ab52b7554d938440a4107a272186f4679d31b1c1d8656f07cb452a2fec1abbd28a3efdb5bc25945563958d6f38faab7dbc0a54b44ba71db2169316f1642f111e69aa0d7126851f264a85c0451f799d8ab080b3528cdd9c1ebebd987e2d660f845313faf4e493b74d93b9839a556bfdbbc2346906e766a5465a8307f232ac0040a56dc969abc2e7519c86815a6d2481944d77175f8f8f6f666f82504f99b5a3368884d07cddc4c92e81ba68ec91ad05f3b9ed1aae4506e3974018c95665ad249f21c6882048854973c71090afb6f8222df5175952b9eaed98f90886b43d36bae4bfc9c8060786309a5cb1c5a47ac26e78f9cabdd84d870041ed102553e8331c06e81fc6646c851d60fbfc4f4df5be47347ee07d3257f923b02331ef9602c566bc2e008b6bc48d9586ce9cf9d3adde5e67cda07655822e00f66b95c32676e22d0ba262d04045a3e6cfa9e2336af3301cc5e23def47e595355271d08650389c4dc7cc25993a84c62b6a95cc700f0eb83231af1bafca03da774c0d8cd4531ee4e3e4a5ef11a5c4819eac5a00fe960da5e4f50c5a9bfc92ad7686ea7d652c3ea9643bfb829bc2932c255b87171d9d3198ed794cb2887291913ae84bc27b5142ca6a2de9eb3a302ff3d8f817d8d7dc2bc9f6bca071599446cfc78cdb58837119f200d44cd16160b4afed472139c34ab8d8f76df4070bf1c520019de28f486e5516fcbb9af1a1d015987fbceae835c0fc165e35b73b9d9608963c0cdd0808cf7ff680ca3bffd60ecdc63ad7e345263253c01df836540bba7319dafcef0f63431899f1c7f58ea4b37ea3d8d4e9a5698c5437a391220707e34af9017e37867bc1ec766c6a24108ef4dbf67130d23eec4f3299a127be649f5d48b478a83d8c81ab29e91e1157327a3f6d2f36957ee21a1f6207a586f3d5a463974967c13e5a219f6d3d3e688587eb8b4d8d914cbf341378d0f25c681026c39fe65403d8d78c764458334141c46098a899d565ab8c8ab1a7464039f72beeb4f0d3b43a69ee063f31881917167fe6e884b681bfe7cc8c03f916b0bbb345ac36c1fc924decfa8ad613bcb3579fa1515ad96495625e259119149aecae93661d32db0d26b051a1cec2e16d984373c5b83ce79b28bb431097894ce01537dbb30c4ad642b7e817b4d4736321504967b70f5c4c8e74fb9c483822668362650dc143325123ce4701502e550fc9c5141534b23240e092287ced7dacac60cd97c01f2d71516c22845b39a943b11264a4f98ac5864ed4e05da850f9336eaa8aab668bce4beef982358d24edb6639d6baeb68c1c37534dcadec3666b18c8440f6eda6cd3045b7f068c193be24b54372a97391a3f143f4b96fbe4a19acb20694c3bc69c2814abe2ffac3514ab6e7371c22025fee43add653981c9fa37af6bb30f27821e4dabdd6d847dff803decc77febda719cdbc1abd4f1002b81db9eb3494f20c1c561100d6babf1e29127a681474a8deb6c44db25bd82fbe9565a1c53918545e07c12e1e9cde07243d5d6e0336134d216dd5b29709cab0057549133fb6a9e95fcdbebe08eeaa5486275870c09a205ca5c32eb7322b20b9bb81a2064ad4d9c8f14c723c97968e319b844a47e81944fcb682cd8edf1d597a4b98a3aeeb029249772b9bb57d5156acb1d1036b3ed700c9495ea285ae5a82d5c788b172eee610a6a1cddb31d9c27a77614b8939f9e014b017580cd6857531bb6d1099e2f171e4a52c3d119addb6816cef1a05f4ec5d9a662dc3d3147effe9391cfe59bda97baa89abd69ad986edf4eaed759ec0913972558a840a9b4fb87dfc5148d26340b61039c1741f87a5e4f858f03c316ac6d5a24e9e42841410adf128d25503a9c3334086a1263eed865c50b7336fdd52f76d72e2a07fec314b353af032bffea9d6a8b74a7b1d3a943c5ffb733dee16bbc179f5192264b5fcd5424a10c3c3e2fc281cca8084b9572c48c8d93e312e3806dd6e7652eb6d2a2a615c34fd018876287aefa6d40f06d321f0b0fa476f31f327a2d80e28162b14e49059b8e6b77577778f61cd60e57099e2629bfda09b44aaa624e9e85e9ce8f2bc9a8b85db28ad19c890f630ca358e4882a1485b0564ccde5028573bb92180ee736ad0fb66e5d02785564c817845bb4dca29444fd7a3b162bfd3bc01b658df1eb35bc8aaa13dd7eff92d4c66a7bebffbae5e7b52331631c0eac829d3b7722949011fa7398d973cf1e6bd863851452a7663ab3da869f6daea5b15973226a4f23daab4d66235078366a3532a1b4285c93c2d072bf1e9ad9b256a3e4bebf2ce55b46c885e78a6c9342e5cb19307b51b5428633300aaf7949570fa37aec730fb0d8e7aea7bfee1d54e13370c9da2e51d7a81c67556a9221175a975dca9728668573e1bf6f519554def0d12cb3a1495f36320cdeacf75cd9f6321c963e372b5b65e7650f158488dfbbd40dcd438d824fdd1ef62318614b64480a822c21197080d90be39520d1b5b59ca1f555d683174371776d7ee08a99679ef2f2a55af7d4e77e08b701ea0c9faf28ebb801067cbc708e951f2621385762f00df797e802f14366ed9aabbed3b5ee3ec0b2b74b566803619a6282de0bb0dbfa16faa4113f90d317ef331f2be8baebc5cc7f301068ed1e7a32f514948c9219618797004b89b4aace04714072ab0e5d57f0a9d06bdd6211811eaa2bbbf76d4ee6200a8afab8c5dd92d331c123b8839cf39cc7cf457f0bf04e35928533145c0a39c562b52f0fdf8abcbffb8bfeb8215fa58f797c0348c8868c876552aa3894eb34807bc62accd2b3f30758b081298bc4c3d4c9ff927efedb828bc629ef622e2edbe178ad70532d081833ab8686f4b5a3e2f6046725d33adc021060962a6be6e410d6e651f75ae03b870a609200c11a549233164ee4b5782c1c6c88ecf8d7a96ef581a4a0c0580cc5c441962ba84ebe9a415f159aef0f78574ec2fb5f0ab295b792b89aeac2df77b3c743c62584b9a119afe81d960c43711436eda67435288f1581727586a29d9c1c98ea945c64626aa7122fc420dc9ecc80d5e36fc8d71f8311b25acd3d11d7bedfa85796d4f02a5e9476845c8806ceb18900902fba4cb1caf338330e465c966120258486c4b3e0aa32b78e88e9502b7da265e804f1f881f81fd948209ca06554af60b631991cf8225759d84d453be06e479a0f8d13186ab504e4de94986f469b97fee6cccbbb54e68336177fdbe678ba93b5bb503516474cb98e4827d20b60266439bcc65b70ed829b45b05b0d7d60da7b099dfdc3cfe5236794c2365e1f655c5e7eb736f761eaa671371687f99e213616a0445dc53ec49144a26cf8041a9ae74772089090fb27e0349b5bce7cb9d9f054e9d5e72a8d1d4581edfecca0dbbdaa3a1cc8fee8b2fb90e25467ec448fa6df4b05fdfd427825fba29d4c718203593f9259c0c593c71a00b901ab3675402f096a748b2d6d66d3b642a9c9f12691108578ea4360001376315c56b99a87f1f7d9401537052532e5b4ba0778aa98f8fe599c4aee56f9e5b882d64e33f101488e10f5502ddeb7f0bd6cdcc6be6c72412ba4e28920bd20cf3d72b805c76b6bff2895bd13150b163587a152c1e1664ec2db9956a87ddbd4471a3c1bb83583b2f93ab7e8eaae739663e45b2354d127346c08191f006b1e67d1b5a7f6714c5abc4bc3a22c5fa11d892a7f1d5fd12655145478c42216c66401172a196bf689181cf37439f738cdf0cbd738b03b7be21966a8a95626eef8de15ed1c6490392090eecfe9930c247eec4ef964d4686ba58a401dd97d1ec2a23b02919a8772acad453c899cbfb88182df74c91b6cdb5265b96b2f4c7ef9ab0635f57803b2b5dbea2d3592c70b2c4fa88a9127c780845d0e7d930565dff12e9eee8094dec44d0b7af5b4e841d5f1da0be424993eac8d405159c1168fb98fef0928ef4a94806661d37e16417de158ce9f52282af51791e78e8c2e0e935fe5151b508a612720eeabe36bfdf7c04c34bf1bd35331088697e53e6b554dc080a1739d55faec48ab7430901c5a37b5c61ab189fe3deb0f2c781d231eb57dfe457b280a2aaac141a27841a4e43973050538062513f15edf39deccb4d6c87791f0475092bd3c5e91384984a0ebedc37daf8e79c727c2f8bf8ef9a79a0c8144dfce77336ad85750e54e9e71bd7cba0ed50866c1d3bedeff85f7b1262be6d8cb2045ce905861dc5fb3b5a113b4253dcff5e9b44f94bbf79af7c82fcf096fe1ccc48a6b96cd5e7fdd3796b87f14c0a55f05e04cf890fbfd4a6388e54d97c622dcd7dd84dd9c1ca288a3fc1654f7d33d192b464c9ed3edb1610bbc7ec142f861c925ba05dbb3e1808320a66553ea320368ec8bdc5f757aa890825673e9d0e80b6b60a0eefad5bdcee3c03ee70db608d24f5e0eb1c64b6ba92f10b58be246655b984b58bf5268504801b4c1cd40c633503d87b907452c332ea16d7be122fdfcf5ffc2fbc950afe5c65f9fad7db166ea7dfe250aef83f2a4da341aead9f40d780fa4562e3e06a47a1b271027add92ffd595638952d101332245d0b94d1ca6bd726d29ae1b495f01868fb79e8505e1caeb7f8379e689add14e2f5efbc750688f4b2f76245efb8addd2458a7957f3829f87138e61370cde7ee72eeee9a0e76cfa79e6d869c3f2e33ad0ef7f6945af98c8659c936ad10c9f66a639c33965a25eb68289e473243c4a8b58132eeecf7fca073b341c6f8e51d88e955ea4b59c5993d57c0d52929d03d7d24504aa6a8865d3766ca9fb619bceac8214c4c6b9bf464493c534c5eef688bcf9538860cf14fc83c4471baf632eb4f8f7ed163fe80966de5f5e7598c495a0d62849ff611a6f308aa6e787752dafe0ea3df7826488ef9040d0b4f05c199ef9e7e2bc39740830cec27e59925a2922f89ebe65089eca31c1f366e300cbc6c239871d7df51975b91a4330850dbde552ef715793df19e8875eb8b9427ee44527a2fb8da0aa347d4305549866d16ff7c608e63781d80e5ab607ff8472bc9d315b8ec6ef1d3e027b9a177a3ff565d21eaa9931aec272136a3c70dbd2335b1bc735f93fc29dd3bba5c5d32f4868f9296436d057756530db0a65b632ed0cb64da888454c5322db6aea62ab1441c8c21bf406782e5b95ba75b6d8e2ceb6cd6c23b9a75de6936a6c3afaddddb0309ea7713a5be55522aacfbf7e07cb5f2e45081f5bf4869fcad36f09a4cd2643a0145972276f7b017c3ea656cb3263c73da1c1ea1c3908ace4a8a0f630a73f0e0fb8db2de4b85c650d9c36edc0d893a0f0bd3c354bba131bafeb8898ba1ec24974d0d5d75b82dcb02fb2f29359fb37d57fed7a9114bdde0e4ece9ee73d4317faef53babfe386399555e41f7dbb19f1b1d076a4373506414e254fd6f2985026ede5774425f82749765337f9cbb20d034778484e0f99cb08abffda6a779adf6097c61ec22ed7a2c1d028ecebf4894db684804ff822fd4efbcdaf5dbf276061faca150e8b9947bb7bd849c10fa8181551706b21421f5acda536144c6b8daf2d3e7ec8c3b5b2b858915b7fae0e188e7df5e6e90d5bdc8cfce90987e01b2bd945c8b2770beca31eb77f393bd6a334ac7a40eaae47e0a675036e591c873fd2b8a78332faead69673db15307e7e23b6eaa55b444571fd402d873e445983c501e9638039fc8c5a7368e7aac86c75fc0396a559e563e733fab6301059931aaed3d97a22000c30ea9323a9d7f000de74d1e578204ccd73f26762def3a8edfff9b5c631ae52a37f4a96346be9ffc2f2da8a9a2ac20fa7509d005a633fd76b7512194a4b8a5076b2f01809ee0543678015475ee4144772b8d2b920f52eeafccd2a7aa8e9b6a6584fe2ceb571abb8f0c9ff0117e6a3e322024718ff2b73f501c4520f1a43820e8b3ee29332275ef68aa602da6a5cc127870a773217642d56cfa1e90e433ce8f30c173f20893c005cae2e2c6e1985974cedd7ef6c07622a0a1d92bf6c1ac7cca43a0c25aeee2f17e3f99d275c6cf7c510bd34cc6a20f0273e3a89a98ebb23322d7fd1ef2c6bd3fc8e4b4e6b130b38c7b343009750608bda2ceef3f764a28fa2f00576b41d2e79d34ed312fbc5351df796f79e7625066cff9a4b1b739df8cacfa7df141ee7086b64dc342808b77f4bfa4f5f20aa1726d085cabe5c2a76a6c000118ea867928e07418600087b0c7a8765a8cf0aa1c844ca5adf6a109cbc8c33502a03b2e339a87473862c09b749af5e83bae5eb411f33b3558072891bac0ab41a2ca027ec82c00d71bab18a2f0b4c53bac438b48f6ed0084516e21bc9497adae3415d180c25b6e9286e58f8697cb83540b3f3cea1ab20159d3c5b0d4f69b888562096639e4483971c5d46631939de3320892ad2e28f567d84cb7127cb2ac86e2cbf80dfbce06b8df580d28c574a4eb82f5de6778cdd8fbc7bb79c4a3bac618c0b0abb1ead50221572d9a60333a26".to_string(), + genesis_string: "Concordium Testnet Version 5".to_string(), + }, + ars_infos: HashMap::from([ + (1, AnonymityRevokerInfo { + identity: 1, + description: Description { + name: "Testnet AR 1".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c58ed5281b5d117cb74068a5deef28f027c9055dd424b07043568ac040a4e51f3307f268a77eaebc36bd4bf7cdbbe238b8".to_string(), + }), + (2, AnonymityRevokerInfo { + identity: 2, + description: Description { + name: "Testnet AR 2".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker 2".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5aefb2334688a2ecc95e7c49e9ccbc7218b5c9e151ac22462d064f564ffa56bb8b3685fcdc8d7d8cb43f43d608e7e8515".to_string(), + }), + (3, AnonymityRevokerInfo { + identity: 3, + description: Description { + name: "Testnet AR 3".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker 3".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a791a28a6d3e7ca0857c0f996f94e65da78b8d9b5de5e32164e291e553ed103bf14d6fab1f21749d59664e34813afe77".to_string(), + }), + ]), + ar_threshold: 2, + prf_key_hex: "486129be498129dea8749ebbf18c5b7c459abc736a8f72e74fcbf0f14e2c96e3".to_string(), + id_cred_sec_hex: "170cd1c77e6f422f6591dd216a4658617908469d22e0b11db185385dd0962114".to_string(), + blinding_randomness_hex: "1eb28bb18810cc3c86ce9db1d87c2ed127bb9addb736c220d01e48bfe64d0119".to_string(), + }; + let res_json = identity_issuance_request_json(params.clone()).unwrap(); + + // Verify constructed request. This unfortunately requires quite a bit of messy back and forth translation via JSON. + let res = serde_json::from_str::(&res_json).unwrap(); + let pre_id_obj_json = serde_json::to_string(&res.value.value).unwrap(); + let pre_id_obj = + serde_json::from_str::>(&pre_id_obj_json).unwrap(); + let ip_info_json = serde_json::to_string(¶ms.ip_info).unwrap(); + let ip_info = serde_json::from_str::>(&ip_info_json).unwrap(); + let ars_infos_json = serde_json::to_string(¶ms.ars_infos).unwrap(); + let ars_infos = serde_json::from_str(&ars_infos_json).unwrap(); + let context_json = serde_json::to_string(¶ms.global_context).unwrap(); + let context = serde_json::from_str(&context_json).unwrap(); + validate_request_v1( + &pre_id_obj, + IpContext { + ip_info: &ip_info, + ars_infos: &ars_infos, + global_context: &context, + }, + ) + .unwrap(); +} + +#[test] +fn identity_recovery_request_for_concordium_ip() { + let params = IdentityRecoveryRequestParameters { + ip_info: IdentityProviderInfo { + identity: 0, + description: Description { + name: "Concordium testnet IP".to_string(), + url: "".to_string(), + description: "Concordium testnet identity provider".to_string(), + }, + verify_key_hex: "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000001e90ba8909d0806c237daf3c4d533ee12c3c279fa9dba217207c14ce949b67ec11723e1768b4c8d8d4300a4e7e2eed8abcb0862bdb11f5ad4a28d0a3fa0e85b689a994c417f5a306a3050370052b4a56aff95349a2c97f749a68085c77acbf1df399d3603227f82698010dee3cecb6855f27edc0fbc9d183a909835bf53a79ad6771aad9950d101940db4012776a06b44596134ae37e01aea98f2333083d4847e8dbf585c835e1d24072681933093fd152f8524eddc85554e1f5f5d463ed3f4c06b5d11c1d79124ab6f89cc556f44659e46590936941a461941e934d26354ef1c1dc7ca53175305b19adcf274a7493dccba69bd98ade4ea154f028f96d73bce9f9dbae14f6cc44e164d0cf4ebc850daf28f0514d8d11526495277066982c33d13dad43482806148cb2c8015637540ca0649c22de2fc3e11bbb015ce2df7ae3cd845b3eed46c50ba4beeaf9afb7834be1138eff0f0d2c991b33e4fd9eda1605f22b2ec492fbdc3ea4f88d66e837f044a10f3abd1edc6f54087cf2589215bafaf4e487d280b2b0e1bb4452dcfa5888a2f8eecd243180ceac2eba4f24b79c2c56c93459a8ee94b4e69a0ccea43def0ef34628acb418315867594ed1179d4e349ddc2b2b4e44f016a93152fe07bb87b7c2737db33b1974cc1559245e96df657abd22bcab4673a711974eebe760032096ee79865bb6b5abaaca2661044ef058aee26ce1cb79bdcdfeeb0b6ea47e731957b5b71ca9b30fcfcf84317351ba9a520f62695b688fc2125c693125c3f07d82536feb477d721eb6ee30eefdaee074434a1de3e7a40b03acf9b42fe1879e7b1411990a08227889821acf06b88c7e086028f1af2adcbaa74eb8936f665b3fd011e08ba7dd888ec898f10114b4659610d1409a64112ee972b7b1f089c1c9cc3be74e6b08a761ee18d0bdd063c4b6327123e0bf9e8dad20dc8fb048e06caf87adf3fda5fabbe8398394051765b7194b28b74280f9d7736a2fdcae133cc29f0466c6edc06df087ed29ab703dd598183b75bbed6b0a71947fe8a18fdcddcd49386558911b1c1362e5b8159ae39192e791e7254321545595abd9ad7e859e80ae3dddb14731a981599f8e7fef5bb77151850fe343f7dd91db35cd00c1d02e7101c5dbfee9d9562e9200ce13e7638a5a92a723299986d22e9699a90c15f26151417ad770c8534a1b54f29dd3b64dc2a8f02374337344e5aca3d292364ee245476def3fb704f8bc065a7a1a6a79ae2387bfb80871460d9f3052e85f7ce042c104325800433728da6f84790b049df5933a59b3c72485bda556b3360efc0d02c91abf672284bf7c77d0638667e13a3dbe17d2793395009983fba2902e7862de908d22f5562eee6b5f7cc59f10a1afa934a3510f4b032f136c78a56dc5d8c1132008808473be726b698a91c0030468b64366026ed779d1a0e9d65508ac22f5800406f084c7fb4b04ab1c4af3dfadb9e51cd7650fcdac64ac2f8d8f8f9e483e8c44b9fa93afcf36c2210110cf04b3f154069abc50df6d8bf6a59da226fc7b329b5fbc921ec66d87159665b9b25ca49712f9067a8dd901342d97aad36a0129336b9a0e51d1174844cd719163266c8b30400886600e9ee9804c4813b4f03072f0a4dd57915f15ef4d8208e609b0d08126f6aa95b97096c96a0b125f097874dd2fb8428b930211575f316cabad5f14432b57d7342f883b120bf2a134c36d45960a9a9a68b8fac820f03e209d48197d467c416fbfd764868c63770a038333542835ad8db288a9f99389e455fe1ccb74a53b00a6665bfc206a9da0c15fb8b588f504ec9675bbd75a223f358545b8d93f31b6be902408000476ed980e7a5e9b13046353342e3081dba9ae0af25e6ac3e0130db1714a2fba6fab008513b4ac1a285c10274cbadab17eb6fa27125f5c3c3752a8ae7f39cd943a7dfa7ebb181cd300a3dc6a69d11591b91d28eeeca9891035693e0b71472015cd1754deed74de61c04982153417c3cd0b060632c8faf7c3dfd6591867dae68923f9a5f3d8190000001e93caae1dc016ba6b674e97ec58366e5d9bbc91020c734f15b9bd9454a53f50d081f4301d80c09b30a0515111a1089db200e59fe3377513e0f2a07d7059854854b9d7ad2febc90154d4191f235e1db4e3fa0690312346c407a960b18723ac568f8ef3b09c3d8574fe3a118815ff80391b71145f822cec0921006a3e2e248e75f4077a5899eb5c94594ecf5d53c9461e5d0697fb336d8463c9c7cafbb5d2f59f4dfe3fa3e93cf1af6bfef4b862e2b3bf6a29f456025829b491b393fea7bb7859e4ab776ecf95abd7fa1e4e03c77525af58d297be183a9437710f72b35d7fe18c9daae5a0b0dedec5d91eaa1cf779044bd304e9b5c3ab9ee20d197a057a328a71326e9301df60c729ac23f4280245c262de278cbd64c9c58e9591044943c20dc51f8c072f4121098bb8949ad7b8b042b98dabadfdee95a825f106690a736f46d518a7db5ba13f5dc6a3fe96eecb5c9f138919ab329cca7582e448db8d51fc30528efde67a4de4ceb1df40d4c3248bf1bb6618a0411dc205e67f192727a82784cd77839b3f91e1c0b96858187c931d78a79ea1e8e63924171d440eb117d9e5696dd9182a24d66c1fc885171901016f92a23a136b07b8033cb163821ea2ceb6917f36a17fde1bf17fd299f0bcc3021f012c6bbcff17df0e6bca3a0aaee4f298161ef78a6de30c0b14bf3623d8fb980e02610d31ec22ce6449f19df75acac7671f17ca75529c118215f3a7ed450ec71e7175c013de7a423cd96425d10024b6470b7b3e90ffd8ed12a19bd0dc61be354fbdfa2b6f42cf7fc427eedff7d90d432d3fa2fca09a13d0b855f0824c081b8f3010e53deabfc255ff6be14e82778e8aafe91eb222668b6352a0761e424e16675b95cd15023ee7a1927e61540c19f32177a7b564834df3ba08f6015b85f3abfd3589a88a6963771f7449137d8d58a98ab1411abdb23196352eca8d820cdf41a9bf4690e8dc37e683a5739d82a933d1295797ea30d0cf5efca3a26fa1eb4c477830ba93d9069eb7477419ab1defc6d57deee580bd707388f971e9a6359ed03b6bbed63ac29f765266ccf6f74631b60088c4d9415eb759611ea07df121917596067e74558180c9d7f77cbb571a29bdc892a1d8431b195e3c8cce609a221a2fea32cf948d5c0aeb37e3a1f1e01668832b1e6dc21435926f28037c079b721115556c5eafe1da66ca9ccb63233fa3e2bd0fbb4718b2d8a904f05a775d60f9f6e7c2891078883306208ebc8bab4c61961707bff16b1ba070e115dd068837591b1d689547e80ea710a954ff5f8a0db3f6fcc06c50e47df20e837cf793f67bc8dd66a060e0030c93b5e1190e7e934e2f85cdc06fbf5728ba85b82aa80eac192d900a4f4881352756e3a86dc9164c82aec566d7dfcb7d6f61d6a6cbf5959b2dc0b5eea15874c9d7c619e227f94fcb235ab786bbab5680500615136434e52cec54cf0568cc57e8191e9379607878e8925e5774dc3b6ecd36b7b1ce6061719fc4649c4457b8a73b1de11df3f475d7d763c6e31e6f1311273f3dd007b5c36ae9641bc1c1287748f9f27a0a3728c90e2ee092c747b96fe49f2e4b438d14dad21cfee9ef30c4d29365ea7c045bd4e1ead7d9a78525d33c9d3ffe81b81016349ab583633a3343f73a56da136996d1fab654f3f8143ce854367a9958e7c7744bb6d3ec0f644dc11aa2e9b91f04e70d89202fc66ed4567e583e0729dd453ce8284c9b077864f2596cbae38c455c492ff8353df4db5891e18ac5fb1825ada5b80e7ffd90ced8b9395149e5d96731438ccaeee7bfd05cce5c9e220f96eef7e15eed4b5665f94ec7e6b22e24bbdc0509be51a5128f5644264641a88a8949038d6bffc860902874d29495ac9c11dae076eda76c541937d07b51f5b56623f6918d4b11dc2751b964062014827cb8480d82bc7802b29b3c86aab59e0ad77c9d1c265bca0c63d098d984f20684ec5897023e6c19cf06a5cc3e5f5ae7c7e45900a6485836207a4454128e9c6eb86f151606321a68a99ad4d0e1235489d0c62ffca462e5be075656ed3bb4788238fb555cda5efe8690192fe7319472655975c5e0ea09b41b8696df8422fe2ae259d8c50709640a135f660af6338b0a18b2e948f4dd2fb355dcaf097d75cd74337c930efba9bb51d40f95424b091cbe4be6447ac28c3540b12ca3e35e076c37b02b5a1661f6e8d925413bf866e5f5316d76706614581963ae622cae906d1d68f076eebfd601668e0c87419e74419f3b0deeece71786f2915b64fc1c97779a953f7cc2770936577581e3938e6cf85f12784beea404abb446754c2bf5016a0ba7862502a587d995f08bd284ebb2f45aa07646bc6e1daefdc5047e2fea41ecc954067ed9e23902d6a8033f68c2b2232f0a9ae7db7a8dc53ef956aab7e04c2b2ffbfb0d031ffd36bf6e4c081a209485bdb755b4a1b677a515cebda98035534df3ca7f1c1127d15acde5c75b77f2cf218df18b8566d75b30488179b18756839b2c88f4d3dd84ea1044abfe791c32ba2dc5d0d7569a9600c58286b3c2c2c475b9bb2138e47895139452e92a941daca3f2bcaa8c3dbd7dffa0252b3ae7421597e385ecf9a036dcdd86e6a1d817f2fc41b1c73cafdf4805f28de37e32df6e8d0b0acc4e57c976c536023961992d7201df5d47a07cd8f8a83faefa3b55443029578cb1ba9dfdddc2acf9172d141de04505c137098f9cf19e4baf82447266861182411a31a1f02a1b418b5330fa225803fd3f56e868dfb17da635f98cc18033856da8a3eefae0f6541d0517ba0f28169521e5d8764d69eda3fe76a7de8d9b45e92c5f16978b67e44ff4edcdca2a4cdef1286e2ea9fbbfb9f6c0a432b45867803ef97384272e1f71a9c11d434a6b7b7448bc64aacf483eb8b496560142ccfdfad1faccba50bd5ff12fda1704622dcbcaf8dc7b07c963b1d429d4da7f5b33903b45ff4020187581edfdb67132f85bf08b133553d708d29c3721fd867b5e170eb70e65c9cf5b344944df2e879760c7037e39383c24028e6648f59f092b5bacaceb5d282f31a3969dbeba350cb907557553e26a7f2fd204487b7a7c2099db4a6ff3e4621974985165227c087c866ab18632b425c94c8a9a297f1099923c2c6003181c0c788b3f63584e9870deef0b4a95260af3d411fa127c9ff56ce9f3d1195485e15cfba7623a21955d7f0d2e5dc2622c4cda3a23e4c9500ba9b46285a7cab36a56c01e4aa371be55c99dc14cd4d386cb65bbf82617b4c83f0574b99da242c6dc814726bdcad4708dbd76b9b42dc44325951b40cfedbc68e744b3f95ecabf0383314764aecab1416e449e00ec8f59552f19e2d689dd3c5d22565f20ed63ab97d95c5694b75858fdbd20920d20629c638eec08a76feb079d2edc1798f3e34b3d7648571dff2f8a9e653d173f196a4bb1c45987312c77b2b41b2448677953aff1671763f2767d7ac7e7a2a605934d751a6c9dbf9351eea1975cd1104504ca02656f7a8b9e3e886768bf4466b7b3b2f64ef89f8946e88b8113314aed94ae9058abb2b8e5fc211d4363b34ca026e6d9ff052ae9e21feb7b23964041bb763f6f93d5b0b0f5499a662eda86adeb0b98d2c1b8db808dedc3001e5af49abc66df3ecb1134277480097137d0f1641d7f7442c78c38079626f7a7b3f0f04e6094681ec8eaaf4e1b2a4d5ad2f5909355f588db016e831eecaef81e05f465323113da5a5f5056481a3923c77ecf237a4a18404de91a9eaace74a2dcf2d66fa1437df048784c9de024ef674e50113f18b100c48f6f4e6a2ba88ef3f8d80c6aa9d08eeeb3e198c1c483c7cfee4b275cc2af6886c85dc551b2e7063a2fe48909a37febdbd5effb4665f2b19992356f92af45121ad6ea3889ab34c4d2b56dee7147090cb7739b42291c11944868a50ea45f671c8fca418e5c396f02eb3e8082f0ebeda069f8518294211aa9e6efea55693e8646a7f214d12de55f0e5fb889f113d99c2211c18e6258e17789b5773479dbd7b905517c375f23d60951444707a5292fadb7da6f53770938decac8580c8584984106e9bc13d5c307e5a78af4e8093bcabfb5e8a2eee50023ff0ece77cad282f2fd14b505b339ab3dbc87eada2a6e6cde041ec5d5ddb32fcab2d94e1335fab55a5d14973b4d73201103b283115a1438d76265dd7a07c6cbb30cf6329d57503fdd92dbdd4efd3fa5a60c57b7d39780e6f846".to_string(), + cdi_verify_key_hex: "2e1cff3988174c379432c1fad7ccfc385c897c4477c06617262cec7193226eca".to_string(), + }, + global_context: GlobalContext { + on_chain_commitment_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a8d45e64b6f917c540eee16c970c3d4b7f3caf48a7746284878e2ace21c82ea44bf84609834625be1f309988ac523fac".to_string(), + bulletproof_generators_hex: "0000010098855a650637f2086157d32536f646b758a3c45a2f299a4dad7ea3dbd1c4cfb4ba42aca5461f8e45aab9112984572cdf8ba9381c58d98d196b1c03e149ec0c8de86098f25d23d32288c695dc7ae015f6506b1c74c218f080aaadaf25bb8f31539510c87e8fed74e784b63b88afba4953cacc94bceb060f2ad22e555cffe6f0131c027429826dd3a4358fd75a06e8f7c5878791f70384a7f3a90f4b7afa45fae6e0fa7153b840f6fc37aed121d6c51225c56d1ce6bbc88096aa3f86e6b3517daa90baffc69b9eb27aaebf87f04ed091412547ec94aa7bf0c8dd826e3cdd621e11dfef6fc667c53a5abc82c163efa51435b3be74f47073511db07c1af7229c78ec38554a3f7e9fa38499201d4fe6c80e867df75f2c540e57d2e15f9f5e18f73c0a804567494aba85653f57f5f990111d680400bdbee2d0678271ce93a119c107458b8d4e557fc870de9cf0ccce6d34a216a6ffbec9581349366706377b436261200834fb2654ad5cd34f5bfe3f3658ae94838ed38033886844008143d823f7e49db43017655c53b983a883948d7401f26ed14daef0dc46da30f57e187ca8e027063174a412cbfc8a7b606b41fcf7f75b698ce9115afe5124e8d72f4b34ce839742a46885ed60af26f24f8d10d46621d78b5772b0314311ed3bb627e93bac47e7eda5ff4d2ef5f98ef7265677a382a1f7b8f9a43d1e563b66b6de94c52c3c87169bd19b6e884c6a28dd6f51ba64bc36ac07926a8d91a64e88a4e19b4c0fe0db8b76c9c99bfcddc05fd5630c54bff85c041fea34a630ffe1e6313bf039477994696db0596f9e2522e04ffdb530147780099d918ee47031db2870c00b25ba6d201b00ef2459e73f6a8219d7a7ff96833bd1c8a3c24285d93b85a321d1b6e175f2d9ef111a7304438b5f5874f064539d27bf8bc46d8e2473d458f957fa206bc417ab5b8ca4dd5f0595e21063c1bbae14f07bec6f12d95a05166b90896f33aa941e0a057989dcc1ed77430a3e6f93ab16f62598b9816dc203db6ecc7fd7989d2763d4ef72abae3c4aa1e1b9c9965f8772a1be640a18486dc0804a2efdd7175316af3220a6c10db0ac0e2913de42c44f2f54a20687d2b22c9636e0ba8d09747f2229ee6ba0fa49c4a328f2ffcc5ff462ddca0de8b1d13cf85ad590183767a9ae4179b3c617ed776cf14abad1964d362747492547359c4b65e78f6dfb452d5a559ac0c24b8affdf13256e43d3213bdda0c056172771cda382f3f31fecb82ee8dc81a48fe74467665c4e34e07b1954cae4ee97270b94a336779d27646348ffa0eb52663aa60bd8b7fb9311d38a7fc1f0f2842081cb81b594dfcdefb44db1caeeac7527c5a48e8a8ecb83de0514ef184f2a82c4110f9f64ea655fb42218f95e27c81a0adb3fa7b441b71cb15113aef7c318d73f911688d34111928fdff181dbc014be35cc7e3e5bd75bf767686933931bab272eac577558b1314206d97611152ae82c1bd56ab92a4378dbe73e76a9fbd90154bd1a8c73c681f70298d15f1574d7038521b4e9b9cfc0d900c2d74cad594b68923607a1b913cb9484e093ab8ef2020ddbbd77d041581487f41e2dea13832e566325f4fb0f9934df94ee068c676e475251659770b93e0e32c7739d3c430db581308817ba091c56a5087c6248ee79baddc3c233e77b3fdbbd73f1a4298a8277055ee32a96b7e83f76e0070ea65c4a1784e9e281fa70fd9771c762a91c7ea014f60c87d09687768c9d0c8277d84e8af2eee505fda2967631a999619dc332d98bd45d40458923de655769aac754bf9081dbe9318b4880bd325e6be713f3aafb65d8e44b9143acdc7f2ba7f15290c84225c77111e0629d0f82f2f367d2641d0c3658fd54e538286787d379252876f71ec4a96ca72fdf8b156059ec50131317b9243eb71c5e84ab6791303ee0ebacd790bb27767701083afa616413b1d5bf0347cc4199943c938d74eda87660dd40405aedafbaf54b1e177117f632deeb6a9bd5227d9b8ba5693625ac68ed7d1604ccffe73e97a45411b99c666dd5eaa90cd021d05f12f84acf60bafe0c98e8ae213fa65c189256c79afb4b3eba970a8ad4e94374dd6f9d258b8c86335f36481affdcc9eb074f1ace01fae937a7d1a92279bdfb1081e029103e48877e4442199593c1b946aaed494885b0bd9d67fb7abc6f41c0f7574c3cc007d29a4ddc0a966270f5432a47e3e5563061b158eedef8bae522cb1f800c6d5f0a85a4e24a68a9bddb99e408d204e56cd994796bd004cdc5871798df2bf068ac48271290a2b3b1f0eba346f8746c79ce2389ed3ced67bb962fcb50876b7a32afca94216adadc0aed990dfd2b558aa5d8f35aa773e710f14f1641ff7eaa9ecab43ca3dbf32e846a90c8a3771d378aeb9277ef864377f7ca79ec7bc9e4b373ac890cbcbcf7c33df157e0cab0fcc7fc4daa2599177bd9d6bfc4be694a8e834be081bc7fccee530fbf6bac5be0cd1656b4c79ba6e121c39640cda83f81d17324da795da00f9a05317224ba827cecea6cbba1459df4a5bf6b84cd009d5864d44e747e3f30866a7c261004f861359e717c414a699461086e9136e29f9973e5b3f008a77fedb184e6835bc50934178b4bbd8bc9ecbb10865789d063ee7781fb2dc7b9b641b73fb172c3aba915c420c1c72cdc999d1fd224bf4d3d8a5ce26fa36100533682964abc2e99368402b0c518df1b6af245837eadb31a5616884c5474d25f57a228ed2bbef64d5a80e37cb20385a0283072a3b7a11b68bef323ea4804608c0eed5ea213caf6feb183558d56666f472a9abd10ddbf49650c5318109f9c5ed8fac7847644e153638e29e5f45dd00da06c9b9aab4af18eda0d0bfdc48ae42efe79d4e7196ef07c4319efdf042724d9aeb2c13b089b1fde77467c32cdf778749370e2c3b84a466bdfd1aa00ca8ac85c0eb29f1ccba6953a8bf877802ca3e3c31ceba8f9ff0a8684b3b118ab11a27182b033847baa372fb6b4c88e9177b8b131cd69f6ff5ec347db17215fb9890fab1c4d265d405ca74e249fafda004f86f2a5c3fbec7cae6afe6c507e089bb2d47a9e421512c016b99cb1cb15a3b8e75f2d8ff4866731585c686871691b5ca89a1351f6a79b159530fb5825f87131b0bcee2573d3932d223a358fcf08c8d4bae92f26d7fe35255c7486484bcb26693174ec2688abf39db5f091c8c09ad804f4ae42ca8b645200f27629ece81c6b5d94ef7646cbbc4b62a716ac6d2ad21f5a7e2297ebed15f2e085859a1002a9e7658b0d0249446ce9e769caba71536b9329a5fea3a15d52344d42d868b0d92a423fa44f8a6c4aebf50fb496c62d03598de39f1fbdb29e098c6a2e2ca59cbb5e37eb021594463a72471683117b957890f8307828168b1a8f2066a5e5f2c6822589272f259b012822b8be2b15c004a2a69b66c2f5ee5d81aa9a7bbdd44d059587bea89d021c734f62828382c9f13210185482694f8b30b46111d426927ec5f10b155f825d852fd995b9c7ff27861f7fee8dd2b654473198504cdcb53154b29f323ac1adde8510beabb8cef6d1a927f2dc844f746edeef9b256ace54dd8e3219b265ae958ced6593ea154b8188bf58b72ff05c036630b6d55141c4ac227a4d966fde1d0ff1ed9748c66127feec86e7839d77c1c56b46e29d2033a7871b4b0e3c53b3cc4ac49dc8eacd0555b338265be1234b81259b3746eae71600c16b7f4bc692f0a1d3dc2ed855c9fb11b2ff5fd9e6361a82d42bd4ba00635d974fa1214aa8da9180cb158347608e0c44bb5b77397d8b233df7be91e7047d28d18ee66472b98c78091eb95760c5b381d6050779ce65fd1508af8df81a3385f185f7941f0b7aa9bcd6023c6e4db7ac3045aee58f3b2d307a5be14e2b59d05333949d9e8dafbe15b671034b7c629c1de1bf560415cf72761d4d7abcbe3b762d9a66b1b630b75c2af751f6a5d37b43e84139aeb418e0b10dec012a32095cf7305bda79bb3853c3cb1fb3c8447ee4306923b6b693020880bb11df8c78f3411f176b2393776d0b9b3ea7eb0cecfaadbcd8112c4c4ad9960001f4d4df210860470a4c89a0fb9a07229ec3476e1221f490ff80314da398ffbefac3a376c53c0db82d6785bc3d3b0cf3ce4bb54cb9b6fdc29b01bca2894debb7de518a61cc953d4f92a9c52d427e2858db633d042ca14028ef771d9c8c62014676a30ed9d5f9f51036a823a388ed0f72321a13dad31bb28fe383e456361159490942382ed3e49f1c420968877a42b7bbd8c3c9e78723017400dd178f0f89065072d7ef5f5e9baffafb2ce55fe7b35a7865957260b1789e3fddc5bebdfc3301e79b5077a92f6048121c557099c329bb270cb90ad47818398bbdddefdfa6b9d9256b8194f51b45c69176d5d7deb6440c9a81bc5ad0c8b2f31720f071a8ddb7907fb161ed658f890540ac56c506b7b98df3a45f6fc062698bc86d3cc1150db00d702b2d1e3c8eae552eab55a7afd5a5a3be2610867cfff07545c8227dd26c502039240cf00ce2e91b7bd7ed6495980639eaf919635c82c5073acdc21aa275ba87cb7280d0c6321c6a4021b1f0ae7bce23c32238b3f486ce27434721df436446f9ddfb9c66bb1f4b2337803850b2606d8954268eaa0ae060f8a6da2a0e7cc315c1f8cdad4e11bdf5165a2d72ad34f976bfc41000afff158f31e9632b898f2e9886b8b92c81bf916861c62f07ea326e94ddc4ff9af51fc0420b0d3c57fe0d14109ec90a72de74147d8ae1eeebe6b565c4ed81ac01d49b0e6a9551e9fdb7a9e040b4bcbad61fe627983462106e1c0b59ce762ce89b4b1100444048e2fe84850db19d63522d83993067cf025ddaf796e2f11d76da5a2b6ef4a409720ac4e97f7c75973584d935ab0e723f415a5622b03ece90173ea58da495f402f5ecc33526d251da5d2eb5558cbabc1634e8aced49d7c3bfb514c20f7284e8a902855b2296ae90ffb84bd0ea96aac846b6c9dd8ce92c0f9d457ab44a381046e7cd50b4958bf4b854bff340be4d41452ae1e3e19c82b99908c0a45968e580823450ffcc67561dfcdf446a57ed8d032ca94ec3e8dc0a181b9faf478275fc353ba104d5c31cb7338cc4c2cd445ffed7025f576ae84e9b08311b77490b6111dacd7e3afa920193cded42246276cb999d5d85f594352592002efa2d34c387fa1901cc0f63c0f6ed141c7d2a48d5bed031a204cca98c4e26ab7210d4ca5ba3e20df6d1b0e1daa69216b3e1ed98f20ad9f527efe75a0e7e2067c85383b7a614945669b442e1f0d7ee07160902ba9ac292fa0e9c62bc1b0e848856aa680898c4769d965441a16cec21ffc2eff717267cf0f9d4e7139b764c72826309e8e0ce8965c6c96953c3ebedb4ab4c9b35c32b7b59f8715f7c790afe8a3c7a09965192baa75262a16650cfa1706f4cdb4d34043ad1d6a53898164d049647dba7be95b7c620c750e3146155f57ccaeb283936ba6b9fe6a6c6fc5fec11b59b2d12da76075e022a2308fea5865ab5cbc12a4898de2204bb218cb6757d1d3d380b488a22e4dca9c9b1d1e2d01e7a9e707a6c09c323973dd639c7d48f25e4b5b5e95b4394a1221484d337a032b34cf77998acf78efe6d7c8c1ca4f83970bd62c0a790f38e405827a71baf31a9c89892f6745e2d29bc13b5b2873c0581df064a96bf64eecdec5504a75c7e03369662e0ad9f6c2d38c4da79b82f62aeebb69392a78a2cf6fe5a1ea8627b77d5ec828f4d445fbf3a51987023218ce4f944f03041d01976a2780fe632238a828414364b8b98aa8d709bab9c2202b144f9edaab822c33d5d8ec5f62517567711cf25c890036700b7f39b130335b4b8d14d4e9ebdb6c4fc015027907b931fe5cd99ad87976e8181fa284a53185f1e982ae24842abf28f23de420376b205222e5067a2ec53814d659485327eec748b821d70f114fae08ba181f320717dc462c88a15370cf8acba5516c093e13102d92c681bda73f3ab192976179dea08875abdc054e389b309d62a9737e05edd489469f63497da850450e1f299aea08649a9ed1982a4f8a3b251fb8cfb4737ce650d125199d07f7acbbd50dd65ab973be9684ee248269ff200e2f36118cb0eb9104b94149f9c91ebb76f9d46ef5c11b4b78bf6331116110dbe6bebb7169d3c637839bcf89b114ed9d765cd6211405e2dbbe78a4ad608a960a051097a65dc0620a3d1143439c4e04cc6eb1453e43904dc9f9db084d52b2d251dcb1d476141b74c627fbff849eda5a0abc85416b97d9888dd3cd88d33104e8f0fe03ade560dbe4baee030af797fe68e45196a2137d79de2ced8e6c2771d6b16a74b2c1044e9d381a0107adb870af01329c413c0a29b88d92b3a3c4a117424e613704917bb2aeb343dc22d96b0831db78b63063f10527cafc6731b32d50e4b6e39f45595cabd413f62117244ffc1d69122b5b1c9bd864303c46cdaa1e76a97448ba5ed266859a744411e29d1ce4b78a037d99d337a7b2f74907b591da8103613a19bfa6cc1f87b9698912b85ddb0ad0d86569f813ed4dfd3a9edfc0803427c648ef49724149e49aaaa98279b2ea7d25e3463be263b6cf518e9bfc456e993c2a3806335f13deaecd3c052e8604a08c045175d044f7544291dd4acce9db398fef80ffd0fc75b28886903d2d39c133a1869b8429c9ec58069384d8639e6d268849bc02f863f431f574ca988b84ae59427e446891cdcbed74a72e2b60601ba707b2c839d85b33320a32c1b1f7514510f70d08c474a7b6155a0ed004a23fee27c6aad072c0c751474948a1397ced8f04c5b50b4be2001f4f52ada7731781b69ed93b7602441344dedcc359c385a897532f3475764e0ed3c80959cd95d47f958d237f39694cdad607b966b0d2b690844646b614fa9fd4f169fb4de7c2ff3b4805402e4408c5ba5be95cff6bb68c79104a58c11aac0f999ec307a230eba3869f9078c453af1539a9cf5f07dd18a10c2f2104a2007d52d55b8ba7e24d97002c461c2f21796055179f403a012554b2eb503bd7f62aa3542df13ce9b520c81f364b7bea0deda55e430ff52e0f7408e43e4f7289d93b2431ef21d80e04ca57538dc17b6a7b2f1729cd52ccf7f8b4d58fb2fbaef5c9b3cb38b34b179742e686b9fcb884286cbb9b35d70083d8dc6e6e9f374d473a79ffddf8547bcfc05d5da6250ff982c8692b51e6b219406d9eb1b39dd2adc8de3dd86510da7c383041ed1e87e5104d33478bb8bd7891e16c72eb23029dfb8b90c98c15377d0928a36a65b01b4e83e5c93e5a804a37513447ca0e64a945c0807ffb5f2ff06f270e9080eb0fe471b4241a9f66235b8066d425ef09e6d46e19717747d80f5a16964690b949b335b4c20d3d15a7eabd7e815856f3463565654eaa243f7a4c619289212014d1e1effd99390d1feb0d9a9a0b101a298aefbfa703582c5c2f6470c673a180cd6bd9adab2e6d044ba3f130e47b7340ba9a358b0600d1a8862a9c330e6ef1782ef1c912a4a60db9522cd3f1b941832abdce920a6bebc950d29e057d6a71e75f9056d601079109bbbb3b7bbc46b413934b902ea7bd0ed115553de7c51ce60ca100272221a880d3d2bb0b49f5b36a246df30b7fcfa3e924b5e0db2c55038289b1e266f91d504c14cc3722881721f46d864c9f5ca7c736ade9d7196c9fc59a4740d59d0bc1cc3845ab33c8ee7e539f2bcd8d7295e88803b1a66552fbe133a585962edc24ef8b8b3ee88d005c5817c9e65249c554a678b71fa1e6dabdea92b1ffcd004e16f289ff1758a491a2ebed7c070423b4dbbd61141d3357ee08bb9be71663d0d2ea59f4322ee39348fe8af117fb424ac49e362ccdeec78e99f7f2eef955f7454ce38c8dd963bc6a43bcf476514a5951637c64a71dcb65351b05c557d7485494cbbd0c206acc04002ca1545d1ad3f70cff600aeffa021fcc63d46b5128d466c87475a6b420af9e4648b3218449886a5f3448d2993609e7055566430b2e3f3d09a22633e19a1dd0418be9f99cee8be8336f575d3a55dc090deabc6786fd0d210b7e9577d69ef79beafbb541f96c6a01c9344653d7f84cef645989c9928b1738b4fb901dc9b9c29f4e0940fadc8403262e953ed8f6d772f1040b6ed52df64f7bddcab26bd6f5575e325fc81ede2c931f46c8cac6beb889961d6b2395d711261f9ddfd6be95e17f1aafa506e7d14bddb132b0821e7b06cc8597c8133878ca983bedabf66d0d2abadf4dfa38015d3e0afe9323c792dfccda5544c17624162ef0554b5cc6d77362208018717a53f44c2bffec644467d6b2bdddfd1ac55973d05edebd6e24c41223b8beceef1ea63410eed001c947429b115b864d785de238fa0e68411e9c5de226bfee699ab981ea86a67935faeb6f571f04d6dd1b73920a4afacea51c89a5561532cdb94a08da8e0bce1da552da943efdaf4a9549984337f0f5c4b42fd81a4f545e00d86f41ac6d54eb554efeba20896f3e1234f41c285636524b666b69a21bf19318a618c95a05bd4754318f696e0671662e9b37cd2393fb709e687a8ff171155d71e6ba436857d522aad38d34382c5b9f2c8e5b94464156cfbbefb3e390a935dfce72b9dbd2197e241b68cd9ef87de8165eb0515ecf534d5bdcd2b6a1540c092e26d99985085a95e31cff50452e89a3de519a0bc9494c66e77434242ef17bb0f4719b65b10cdead9b2e9072c59d334cb43827482fb92876e6fdb66dded5245e0f4a8aa6ac7bea417f3feddba61ee20b092fda1502551fe6cd0f14023fefe4d08767a0eaae297faf7352badf5ff9e58cf4d1fecb857ac3b9e83c90d19345474f82c074da1600172969606801ec567df6dbaa2df8a4380d3d500570b8461b2cc8511e5d3a45495b3bbe014440846892b11c3248decfa722419cd30aa94d9209872872820ad1b02b8abbce3b26e31aae5bcc816795d0bb3810c1e4f6ad22ff0a00ad709f93c3f63a9dafc2eb7cc6c8d986808e165b25dbed2af7ff5620ab09b3aae0928ea163fab6315b7885448f5579c40c35ed50340bef058ab56186940cb5fadc5b1d093c1a82ddd172a4d5cf3efd5c5297db8dba62e1a302127b7faac192a1de5f8225c49607768a7561dc138b0c4d9888fe68a5e38c125dcf23c7d9066c34c7536226cf68e8106c9eecf4925e48c6fb8286c5e4d48c136550d179a30daf27017aaa890abdcab582f67f4b7117257ffefc0bb8a8b6bab8c68894955bc13c185b64b5e790483145915881643222c50fc024002a7c619ac5561bb72704c2d46745ca5ee77311a3c22750ee8cadcc77dd037db697b939ce0e8be03f83c82948d491b38289483c8da893ebd9384f83f1ce0148a43065ea4e041c22aaac15a1a82dc6688c3f73f86f97c97ca7e259cf588f8ddf2de829970a0cc527df4d9514032a4161e3175c34dc46065c3a1f4c70a07734fa9bf76a3744d2764df6fb49dd8a97d85c62890d6cc094862a34ca78558c47c263fdeb1dd5dfa9b6c0ae2b3cc43b5fb742049ab38e87f9d50ef256fe3187506f8b462ac07b85f2395089291336d8233db5631cb1afc311f6b1bda3a2b8cbeecdf46085c5f9c0727f40c64681ff0adadfbc742a8a70d717a00080277f8fb67074c98ab4cdd2dba5b9a6d3defa9b6024bb7c452f19b74dc8363cc33b62ff9d5653f7f069723f1d6d7f7fdc38980bcd01d1867c2060dbc75a615933c2830e5a39885185d74f980c733afd19a830b12dadd006530b1514b5ef61de8b5b27ca58b9471029edf360e8bc4cc962d6ab28a1dc2083c5b98510f4c22f4577b9c416b4072f36b298ee1ce8dd8111c254340123e19509c179e4b71cb268381af7290196666e055a7de2715e6cc173bfa5af642a3fa942c85b07fac2f9970bacfa03e09d36da2f80b4f1a379e8e5b43d9e2890d551e6f37df150fe561212a3b0b714d13b46765dcbbac3f6ea01c976cd366da0de6bf601eb17230e6f3032f9a476ceb75d7d25ceab9f65bf93102daed84af1c7e336daf571bae47f98f74214d93403f55adb50693c9cdc1a2144f1544bcd1e441a34a8d2a2f48387130e569a9f3053d510e2bf379df90ed2064ebd503b44a2a729a19ae1af257508dee6133bc8cb0af76690be0b41248f3f447adb4679cbc91e93532513a9cac6dd4a587ffef01c202f6e46c3129567c833b5db32fb36054cef0ed13530c48cd3bb887137f41902fde6e4e472bd6e38f6a0992d363a0470a1f8f98ee17d528a0b4b187d6b0a931f2a0b123d50827d6b50797da8029a868f97e144f7586afb6b394dec7ca89d85be8a0ecec25863eb9215704eaafab2e277c3256dffadc6a99fb3bc94b4483a3fce40f29ecbe39c223a9d99adab90973c65ec64b9b9ba9f1d68ca967d87df7793323d898fd09065da2c6051308cd4e535634e235e6284fdb3697bd285eae40d57b976d7ac690e6a96b193a9c3ab3dfd771efce5cfaf12d48abd69023d7773ae998e7433600924435bc89163b7a18f7bada60f52ced09e50dd8401198fd48ad6741f545fa1b53ddd0b55e71baa55112e01f42731148ad13f696b63e307f3f9878c74bf513fb03b3bbcedd2234f8d299286add8e0f7aa4c7511b9cd080e293b0067b4bed86869a814fe4b2a5b5f2a9eb543795c7139211a61de2d5dad8c61969a0a460552589867d544b3561b8234e7d16ef4fe6590d141e3b29a77e967caaa25bfd1eed4537e5d57372e621f4f99825f99ff7016193038ad5f975fa64497d20f2d05ba4fd2d015ce6ae8d65d536584157d0a4269f5421c308818415235d15c2063bdd645329e270c4c6e180575ba5b20e98812520a41d415df1b7acbaa29d6600b797160d9e4b7f96cffbadc623456aaf9aeaad3fbdfebaed9b7725f4c5027d7acbdf36c3aa77ba6d2f834b5b45b2e7f1538cfc109a4817c913846a4d5a479d5f29e9b377144bb76265d1747644dda7465b498b3ebc4d361ac8817071b60ee23d0803d19a9da55619b430997ebc41c59d8feda62b9e1f287634b1b8b22f415c89444c01af938c50e0fc828996a044d5dc514479fde095d6e91c2e75e74af4dadd159e4e84d9dfb8e6e4e3fffd7f9a1dfebeb14816857dd1b6e197fe33d03f1835f91d2764d3cf8eea43ed03e9d4486cf3dc90f34a4f4e7f1e6b62dec4936284054322904ec54c8e3d618b4c8fea8c5985624b3625b01a904512da4cf3a5bb5d23d99fc6f72f636e08c23d435de96b0d52f16b68932e2a03b0ae8de987e2b9a324c3c1028236ffbbc85399dd8e441ce740ab549ed1547591469251aebad65b00fd4d60abf9e3072213e3ab5ecfa9ea0c1dd98141b6f58d244a6979af521e1d1c04b2de3db33a15b7e4845046a4f749b9a08fac86c32b958079a48a13743777aa000581a58151b3bd18f5b397a786d3323bce206a91013fa451a2e8c0c56cd428497e959b728e88a9707a80230850f82388c5f653adcedfee27ef1d045703dfa12952f9ab8859c610f3a9eddcfa6f3c470b7ec1cad5c06f69a65fa5e8641eff14a5378fdb63099cffdecb4816f5fe8fe64f715b432301523d2280be3e3bb2ff3374c5fe07f1342a37fef2b46a2726d308f9e678246448528a219ddde24baf73cbf4459791f564d9fc06b22635a0580b3ff21c9d5f6ec4433fac3eaddb055714aaceb48ac87af41f00fe3482a9888afa7a0811cbecbe64b264197ae22c1e17baae62116e79948771731bce85068dfa4016f3978e39ddb6373b398939642824f763dbbfdc002af17846352ea59cffbb3b07fd526936f313a9f36c818b9c8935d3535e05a7f8ad66541a202b3770e7b5b9ee000fc03f87a67ec1668c270bb6dbb46e1f58e999887cdaf5bfd08a899afbd1b1712a474fe6bdc0e3c6540072ede5b4fd2eb4da9b93bbda506a634eae7f9b0855c40609011911e765c5bf92c7ef9876d3898d1b9f74ae44dd10de3cc31b5056f219ccb522f6c48d8e32893118f6fdef1441251f5f1ec351b2ee7b95a2ae8d08edc4ce2d5749b2f07321ea546805fc4d46c27392fb66ec9ee04c41c8f77d4920dcbe5937cb5507795210b0acb300b3a9625fc4edb076cfd36c5f8f0dd519025af2eaccbc13747e1c7a848a5deafa9caddd682356e21d7d24d7b3458f8c2351c20a864a152fffa4d049b331de77b2de9490bbede98488158108571cb80c5e3eff895ba2a31a1d70aa1bc3b38cdf5610495f432a9f9369b9ceacbf45da63cf533e42df66df7c7e108146bd107fd890e32252e5e1125db9e5e938fc86a3ad8547951fcaacb69789e99bff56fb9e0350ec742665cae725faba2dd08de44a16d5779fd5c0a3624db0bd265d933494020efe58b64b6da26b0a78377fe8d55a2576e245791efbd9c2811c66374f1d95121ba4ee1b85b637fcbad348cc8b089936520a1c19391ec9186429689919c0692b665e95450bb37c8e88306f1d367cdde245b2174277da0cd799788d6ee9ae8453adcfccc3a5dc9832cdc59946ffbce28be4a4c0630691f5e9bb4f9277df568182fe478a4eac8b6576909c6780a7bcb0ce8f7d60f89e2d76d1fbff45d6390c1ac743129754aa2b7320101e0e7da7327d6c5e545feb5a0c48c6cacaf245503f87d7b7ad7ddb24522df40f94cea9a9044ee7de7216d533e16823ee8599181975253e5841c4a88f71b04d9465eee909c6b1e220d11da5bcfd01ab009ddda154828467adfa4c49cde3991a2062e10e74da1a6535ced9fed0b6d9f4c4f507d711d8879b883c43e7c3588dfc02a3218925feabe7dfc619d110bd5455cb79ad9cea0cbb5d2220c5d83c9a2169af546b260c4eb936a74db8d1538470c73fba41010963314b1ab662230d2bf126102eca5a8e02cf3fc9c3e406110d44c483b5a1770c0c97657b251ebfc59d2db5eac1cb2715aa3c5b65c1f4eae95b700bc4815b663b61d0cc7b5bc2b8270d7cd558edfbe3086cac8eff197ed25b4ca98546735f433db5ccf9d0b01f56400f9eae1656bfdaf81137c306211011e09b15dfbae35c8df774a3bba8e40cd3d4fb00a6880e445324f26d61a612acbdd0737cae1179bf26bc29b7cb0b23a907ea011402947c0cebd8aaa82620a36ab9b6d281470d6f05a42c99b87981f7c45adcd01b2b47026eac614e0930976ba5b0ca56e81fc5277d80a9c7ab49d670ed03dd760af98a47e495a3fe92c0f97c4138958044faf55c5f4ab7833f1db1245522924609028ce1c234bae58d99e377eb82856c7a8fbfc6a21f3f2c21001badd0aab473cfdecd9a89d230ff477d43739debadf3c7d03de3987d4715ac54ccfbc271ef5796428faee43cb46feea3a73ca2acd73685d6be66e86a958a8f29ddf8a7d3f9f444b399dfab5a6e46aff3d3ae8d25a4c737e4c2bd1660b0302d2381ee4dc9980fe75e39da89af19310d61a4042f56d22fa2eab5499eb2865197ef07226c05600fd904f67feabb07a7b6f5fcd31302755a19c28f3fd8fb34849afc27a79c226e91645642229a0415ecb6279770561ea342e043baf8907a6e6a31914bfd8295ba6926bf4cfa11a369e10895d5d4ced4c426b69f6084a304804083020e812d01b0b6e6591dea9145ae40fe4fc0e820c2a3dc813bd6b1e960adb54628f1fc5b66df7e33cc9cfefcefaf14a510ecc1ee5a7bd5656a34b14d3bbd6e20034b421233e24d7d8d80b51aab12a1d55408e3679bb3c68997729b2488594ba5d8ac0deab6b93fc38ee6db89eaa263d34822bc71ca45234db23ec5a6826540781358a9f9f360b863204e81d07046a093dcb0c9bc3c0415b7c45722eee2ac88627823b78421d11f0be2d32590c2380f32d355229e34e33e44ea3ad47be9021aa521b3832b8edde4a37368ee10cc09141822d5dba7c5b50705b4af3e94e3f9ca32840146fd4f5f6910de5004fd23c3089556a0fa92e2645b9a3b9f4ac9e850a3d3e7b5b8d32fc8bd9ebd30d9d5230b04c9f21073dec23366748560c6fc67d741a822c54ebe373e676e515959056a805f09852a111a6beedec8c1ed6d8133e24d04fafeab7b877b71bd107db03e7a47219111a98d8967405396fa2a12e30e5c02b0235d6ab4c414d14393e6bafe123ba93f1c24fa303313ecd123af3c816a898e8c015c977550012256d83c8f96da6129de1b2bd857afee09a2122c264f5535ee17207e07250e756051b665c5130945b98cba09141f4175a1dbfc6fc3440f6a2692f0141a980b856131e75a7f0072305c0065b5b0b32d0a8a406f136a67d8bb51974a46cc1fcaebeede69c8f76e9cddc5c040ef380555ade3492dde6f9caa0d4c18d02106389c3334dd85e21e5d1198bd95e32a97fcb59c85de999cb078d9d2982408cae15ae6fe9fa95b4b5443e8d6effcf4b3e903f021f42644d1605bd52861a520f413bcf9c5fba40195bd534ced9088357fc3155c7606ec5b857eed52106d4472dfd4220c46c88a2e444b0f0ffee688cd472ff90304853bacbe594e7e9eec897b6f6fb0757a658f76ac8a36f995f228db232ff365c842a95ce4dedeccc7d6adcc66f9723b413de9f410bb3a62b338b3bae5e9115fa66d581a5d459ec0bcb09a013e63303e89073cb8473896915f1593c9a62c060a64c8d164cae46388ce6751a7d11e1246af906e1fac401016bc7b8ba49be563af3fe509c1efc2771bb2fab8827c60cb79de9a1ad7485cbf6f145c4117a93b2f476300c403a1fbc48af19666556697857a6422675ee39868e4d694b538a5d90a741829ea1eb7f16a4cb74dd92eb71cfa84b5ed5eef1f5a52232b53d8791b48fb98cb0bf41a881f11d0d202a40d5031ac03961c345750b8d0846f28f76e663b931c0aed8b5dab097d144826ebe16b8e4362868b4b105ae28e4c458982b0861c0c5ee362ca4a714dff9ce082629e21c612546e4cc50fc78dec5ff9bb6cfb7477499e90b3d799fcd26bc460448ae038f4d7dfae99e1ff63d34285a15592955e50691548ec56edf4493f54e90abe856c933b365953c683767622c31bd2b18c50eab5e1e8ae29cba3dbad2199a4fedeac65f4fcb0094524ef23a1d0001147cf902c092e82eb5e2b80340ca64adda9d479af18673875eb661b3f085a016b7fd21bee7789755ddb38555338d36bf6277947fc8debe80d3ca65934882e6b03ae5426dcbfb40efe0de27d5dd33ffe91d8151327b8d49232495a021c7d52b1a70810d270f473b9c0a9481744b0b51ba5a3b7f1f8f86c2f23da41f61c2bb60b545bafb0f7294aa9867e2d1c2c5a1b4bcad31373e8fcd29118f455d0334c9db169a438fc9ef021aac8d8efcedd2fa51750706d4a4fb95d7bb5d91e86d6b8b10b87bd82aaa18c2074e54f9ebfd46c1659a988015c86b7ce187b83fa89e27ba3e91b6e1c1bad902723f0f1a05a9d016e0e6b587ac9b3f1c9985d1ab1975a5427bf34b812e87130b44812cd2e4c5dffc0752c13c523bfc86baaf12a256141f12f33a727abe44c948d58aa76f91df64f063c36b9a6d04dcdf7201dd7c3ffa1d45eaee5ed72b85dc589bf8147ef5fef4ae4b949c2f9add64cc0b45b05723ee3701165b4a5eb8a442dc0af576512b0272338702afaaf13c3fdcaf4854a010733c4c56d1abfabb061cc40b0dab4b6c5861fffe5e3b8294d393a368c38196036b257f70d227b32140708516a1b97c9c50ddba5ab246eaacb193129cff1a51ba69b4744b712ad3477c6fe36a940b6aa5ced1615fd0f9e25ae89812eb682b8a7a376c1e8d08e589cd9e3d29b25ac51e3e8e3ce7d4b3f8b7fe1ac105c7ee26e3f467cddfd77a5da729943a14ce537cf7f54170293996834a4206521a03da2e14bf28ff11ec8288912d13a08631e01278eb5fd631c7ed1f2a2c72f22f7d7b96cb27c27da9ee304f70753287d4e288e53b2781b8dc405379154da26904290850422ff4b396a1352ebe08c7136efe738fe6a38d39b4275127b9f230f437cea7df223b3b31bbd88aeaae1f7ab5e93d39b7f4efc5f2bcb6fafa8ab485f9c121cf9526fca23f3213c5ac138190c9458236557618eb79c0227678f9fa5557a9c214530bf6b858cdb46cb259092425ce63de420298a7ec7ee62b8b5eb61a3df4a479b68e0ccf7ae1cf4908c0b5ff2f34383fbf155c94288614ae02b8f1c4d30fe332ac7a87d45bf55c9fe2fd593d6160b92795ce24cebac2ee6c2803087515b8d44c9a5953bcef88124af2e82f01e66a2ed8e3d68260ab9dfa887036954d3bb9d9b43860bd482a5c8ef96aac05909de00e73d96063256dc875c08327725ae2c71259001b49acb348cc3e2db38372a67cb2fbcb136be5597a6207e801b08430bb5c33e9d1880621a7508960cd3df0c36ccfc9d0bf417fb43239a72490b1d536beb736ee74d37e4745d15b1a860c7c43532c51621414ea153daf68fb09f0eeb8915f0e973013dffdba60c8d7bc807651ec4227c0aa4fab2b04001fa8d27b3743a75e91579551d1c45d1b47994783e5b9b79eba1245f2c779e856343fb34b15b9ef2bc2fc7db25df53f0880184b6afbd88813ca38930d58c6bc371e1e0c6729baff00def2818eaa51ba356d6baddab556c59f198695d58621ca609d187d9937bd4dbaf0a55dfe855d2c6f42df4906eec6565c283684b51f36d642cfc88841292b899f4ffbe6c7bbed34db11ba1646de299d752cfaf3ffb98c163ba8affa22b4b96221991b48a9e51dd5135042e2e90298088da604b8cf7756f6c9904a55ab157f508a028de1cfb65e81ea8065f20b7ed44c15a8687351bdf200aceeadd5542e9e33dd85079a46b54ee44712d8cdf2b7a672d1607171a9c00221cb04c256f0a355d7c98cecc9035ad45032fa6a053333e419250f4b57a026d72b341fd90c4ee43808c70569058a7f391962f39158d84d0c684fa4213551d76fca21d56836a64eb638b1a1b8b9f9d22b2bcaf3bafd6d4ac633dff5556f622ee96f4512aade6f336de2ceafa3efe78d20a4e5a66549a503a44fcc91704b5e865f451fafd8ecddda1d09315f2f2938df845c1f4bbc55615f1d3fff74cd263b6a60b5ca1ed8fdc3e1d69f792ed688d95cd679873ce958458990f2aff5dc86292744cb698d93a37da64965572f663cd4b2203a80685bfb55936bba99b9a6c2e0d6cad1e84b6b62632110dce3b4995f481cdb308b9b9c13f2cee0c92f552b148eb2493c63ff3c8520c4b6660522f7d314f6e5615b81e97c4febd1ddd66df9617814b0f84324d683dbfd082d803ae2e6fbd76ef66fd9a24f1e63c3f3335a2f61554f8be4dd0cc888afa85645372da0ee6d6730d32e5b5c7637564e7590d73f3a86d5a7cfc3fb0079faec67f8f6891d70936993d0b3dd6a385c98b45830b3ec76864347b3fbacc90779cc5fde43af74b5c327502fc6e2f0c4e0533d4d30aeca71273a54aeb815699d38781fdb6871992591104a2617fe6dfb5c628d95d1f6caa29194305f5fe15cfc76d1d18509fba3a09c208aada3edb8fbfadcec634f30866727260bdbd3bfd751700bf9f78a3e896ad23db343877e7ca7b8f0af80cd97ce764ef93c8f5eeae86cbfc196c4137bd0d5134888ec2247e62297928ffe4105c1e40d865264d1d046ae2e785dc369daa550c4861ef870913916694bd0baf4257662b413356ea72cfba8872eb522c6e1f7db5604916d6ecf9d4c8f74999f45af1b486a886c8cf6cc7b8cb8de8b6f9a7e1a6d445a1a0cd69cc3047f4a49a78cb9f0af584d2508c1708c912de3e9567c2d93a5dfa595199aeea8e9600e9193bec7f61b7bc5032b59c653f654b1a2385b5f11266ae2b7cc9ee7154a2ec5b82be245f37aa82b7af41d11e827f965f7819937d2cfafb34e2d67f8afcd63a49c68c2882937378e7d5b1d89bb72f4eb341ba6feac61fab677d42feb8899f763db4c1dba15014c852edeae1ebb9218b36fc465a4f29d6bad08821f7838777559953dfb9b80a164de1f0b757f193be8a8572231c799bffa981a89afde411e7789a567a462d7d0fcd351695f09bbbc46fd96fe4b875135110c740062992867616e920a078148dee0d87a47161c84de9b4061480cf6c184bdfd8b2dfc0d668287d7206b52ea84abe8692c9bda5318fddcc59555cc856fa88795cf981f987fe6a2ec5382bc9bfd9e5c61a9bd29d6c30b72509911980ed85e5f1a2baf1d630b59ed80eae8b7bb8811c1fb43115bf0255a5608345e00a401b82938c5def0a553b921d4a33998dab73aef8dcc94a84629876be7ca1222bb03623c2a684f95acd7cf0829ecd1cb1323a3c2837476b9563f9d3deef4898bc50b48d062eb55f77fba9b929805a612b004e4d67ed7dc3db2de2ff23e696b525334e3645015e3a5926ba734e3a615c037d5795d180fe269ede800d6d505c92b08a35331e49d8e91fd41fadad67480489a1fc165ff593700bab7b98daaf6f47dae85c36e267e8517f7992517da7f35c4a741128b08484d34d9468fe66977bfd06500e286cf246d65a8121be3a6392ea1c011f110882c2c2b6ea8ac13858864fb1d8ec8d3b00238bdf7f17b0e1719b13642424337a35e188503dc4a5439876e201158a082c2035e2c39cac23abf0966a44c659bff7580e063fc565db8d0b319afc7e16062b3bf5f840cbaa07b589d0a68dfcfe75b78d67b2a76e97dcd72a5450dad5ee69b61e4e7717afe68e92d03f0d992a3568590d028d1b4382644b383756541dd7562216671b936ba8fcb01243f03ed4b540fd2a3ab2834583419899b72f5912b4a77e39b638db313eac03b17de82f4576f1578762b83948a7f86ec118a1cfb545d3bbeb33b9f54e2171b880fa9f4a312b721cea7d58b8626cf3ec13c1f1e9d0ad59682400d18beb96b512e5f6d5564926565afe7586162180f6dffbce79600eeb81cfc7465c5820d40993e3b1cc69cb79ee6342dde0c93b89a4b0b57e907ecbcef47f5332fc0fe265d7f4a813cde04f2e32f24b541f6d0f28ba53d632a9317bca045afb660ff41986b421ec5693a0b8c78279f636118e618f192330ecf34f533d3af8f6529bcf5919cb2d4222e45b546ad303f27f655f4d93a867fd929cba132a460b7063146633826809f051392d0c9362408c6e21182108e72538a04b6bf8c80745b3d1446e15b80c26cd25fca5a2f06e2afa0b73041d5e020d8114aed6dcfa8f3692aa08fae2cdc0fa49630bc4520b1c70dc4ff22b9ab64d589a1c8b9d9b15a87c8e1514f7f328db92b0ff5b1b56a95c136b0302697309bc92a0dde9f69db9527ee1d5b42aa2ae05d31d5f84ae266219c8ad8de036bec43d3b65b326645430abf28f3a578167c2b2abe0f20eaad0cc78735bfc0159598070ec2ee2a448a52492bedc6f5743cdaac00a5e0b493a82f16ded5a7760b4ba7db565a96aaa2629caed538d23999286b0395a59b6a32419413b3e3ef5e52c504faae2f54f3801389ff2aba485af2c46cd1218d94814d46dc6733590be1e559da9e71f45a60faffa607d39b790a4c14545b18f6aa9f38f9149451273ddf464501dfd67a55c0f95b9bcb7d378d523c52d80b1066ef5e51baa34bc5575589586ff31c8beec4473cf78072ad7bbb3191abc15c701d3d8f390e91dd0d99d02dcc7129358895ef6b31544fa071e56b8f9bbd74e023b6358f4c7452bc6c22400029f53fff4819f986c287fc4f26c8f071565faa3e1da2b6438745512949996c88b54e39c0dc72f389f7aac0e69158e10906f9ad41db237c507b8e7b0f24f1815c23ed0a9b2da7618196b0b6f50bf8a9b941112b2264709ed2797791d63ac0ea0257aa37a8e3353a2dfb0e90000ea384459a465b6cff2f0671046bac98042c3b9ea9f4432808c6354ab0adf0063448e7d9cc9bdc53963d183411dbfb7209b4ece4b6250a5861364347b4f4bad8de949d5413dd4651071db6eb77c6fa730d312754f3541ccd81c29f24464cc4d370fc6504e6710902538eb75fbad327d6e74c90b2b346dfeb9234522f5e4735259222088b92e1402b4b9e9a2d5ddbd6aab5e47c8abd3f7905d52a832d701e44f8c03b85410206cb5432fdc6b925182f119663f312ec175806c6ade1fa07ae9a43cfa2f7495775ca947d66c3faa0a087620f78cbeaff99955f762e8d82a2ec20c2a5282a87f716c96a5d6a5de2043713e197399bfcaafdaa75d5579ec6514708e60cd6c7a52b9ff57efbe70ab973a5a5ddb7e23cbf19906de5ece03fd1b7c58436ae574bb51b467ac65a0b5684c4d625a3776b10590fac1a9ce8fbabac4bbc369f58492a4734bc57644fb4ff3bdf090fd4db40afb5fd39d8419b3db20b090e766ef4d24fd28a698e12bd94c1961a6d0d26404865e920b5a08dcbb9dda04391b4bb9c4262522139195ace73343bcd2ab056167ed0ff0b98299dec424970d87d325a2bf52cd3ad2641e3459996dcfa0003dd17de89f1c457dac80e3beed0ce574491848762e1163e6d3fc7e876f0209fe6cd101e45b99f7943cab69f6186d9bf6d8e520d7539763f5aa2fb4e43491e6c2142608dbed4c91fc1db473b3576b8e3c1fff7432112e99988ca7a6831a9242eb3b411f03d215e1748b4d15d8188a7e631acc38d59c3d144c1335816a22e0cb5ba99d7be75310a893bb59dff8fc1689c64088723aebe4da7da792ac20e8ff5d4dad89a5ad69f29f596c73bc3a4313eb3bc25312e255f4c895ace26cca38bdf7afa9698ad4a410dadfa286aeb8d5f10c467b6df332127c682b989308b9ac6d77866cf9c9cab9c7148338a4ce3b9e39ebc4d7e1dd5abcd5ecdbb25dcd41a74eb40afa638f6ee1ebbaa31598d5e533147dbed0f4121b9759f32444935800ff21b23f1a4f53daf22d45e680637bc82eaff186540ffbd82831965377fc5374ecc057fab5b606d45df49d1e46b7ea15c42b77740348d3deba6eb02784f09443e251be7beec1728db61c8e6b7a46d9f61c25854c8cc8262afd14a20a37de4cd8bfec05d5a3d603b3f0665c5fa21be03054cd70773fe92364fbc2776b591a279fe000bc52ff0de9b5995ffefa0ab11324ecb4d7848761bf855fd8149a896c9ba8263cc27a90ebcd2f9480cba5ff5d4726cacbbb55562692c74645a2df6e0c965bcc907a7a243e5e6699af637fb252ad071c47e2d5f5538c4d4301c9c176bff0abea559b6c42bf1d0ce04873b99350605bad00e0577967414820a8e5f5b2316d868c9dd5cfd40b2ef35ca7bfec53aeb32a94dd9341b8d629200518776874aae6c8e3281196458645fd97f223da99034b4b3324bcd1a4c51538d363eb2f3b46713831f58fc5411939b6bfde6e0cdc72417090fb4e0905831ba58fd8ea4ea0b9039757e5c83bf635977770bdc802554b54ecdca275dbd921aeeefc84c934f19960f7125a926ef85e30f87b045c2faa23639860a5611d3b4b88deb464d8ae37d53c0125892e226024fb7b1282819f282531dd36ba9f1d6abdba037acf1f43a3fc65f793a858991289a2d00417f8b09fec15c7c0a3d863e583069b93a5b0789150ecaa8be5d54cb88625487eda5dc5996a95c2e15a46fe4ac3ffaee66c0cdb16bbc7ef90894bc427aedc5beb2144c02f6fa9ed8d42a51229eabab54ba7792dadbc0f11b73c18fe5a15610f4530e54610104d7b8ccc14108b8fc4a19a0573250200b070f41a8e5fea8826f0173d8965adcbe90d3810e2e35e9ab038f2631bbcc18af94177a0f1c0af02566971af4ffd6f5bff94a3d5548f9fd9ed9bdee151a4a3f6202a9271fcb4635dd915e0a512df7ca7d60047cb6dc5c128df391875ef3b09701ce7165c9798f2604fd25eb078f2992ce671cadd4313054d80462090290e1a62164dc5ac96f45c0f12654c797f6269a37b12ec4e3b1e5e4d9e5dd17f5262ab1a7ebb873f035aa268ef9e374d6521aae6d7af2d6d65d836e79d6461c5aafe2c91ec35374a038b9ec89748c1693122ac6028d391d6f5143b74b842f53493d5bf6956d26a33c63c9b10994034a36001ddde26e5b8b6c1b8ee76bb94748b1fbe8d2e08fcfc31e2fc459f518282a609c1f97296185ae280c4318137bc411b8d097c40af0a11c2685e1ea3b37f425bb4c78766a235d219531d2cacfc12404ce3de0ddd4712f84671acae9bb139816bbcffedc1a6c273f4d86457af7aff6132e94cb4165e90b16491df1e67ffc0fca1c227a10be9c46447cfd0005a0153fb3b535858ee2cce73ec76ee4aecb5b73cfe4565a1a0ed1f5d582b1e7d244a413e93f5ada361a58443fbe0edc99c1ee3368c7583b70c21bab896491c86aea126b6779294ad1b36fa07ea2b780d4a19c5b0ecef284d444537bbe741c4cfd6987ce20f6c6765ee90d38fc7f2d587d3a40cfd79f312ac44ac734adcd418741d2bb793a468ae43c854ba982e9f5667a488e40b4aeba0eaf81e17b0fd0c76a3326591ce94be7290f30bb23307dc78d6b69975455d6cd925b09bfcbb43a591f9b509508138f6f04aa7680111ea6a280195cf3af9ca06958cc7954db0c53545f473207520acf19153c1219c25b14bd52d9e8ef2efe8fd44716de55f4f3316970ce80076bbc4ca37b93ae358389c5b72f0eaaabd6b500ce63e26fea094dc76e4e7efc0adac93388f5b501f572b9b482d6586a9f9a678fd5f90491eb94dc7359bcb27308b74bfd4541e13c76c29ed6b425beb4a2b68ceeb49503fc4226e980d0fdc1dcaa97f962b2dae3fa4628e1161be82d56b87234ed079daf0cdb95b8eff084f5dc693ab6bc906befb0323eac3974cea284647cf84c3398d1289a4c798daf996d8a1a77b0e326c8241fdc35aae4112f0d306691b533fea134585d93f86239d4aec0907c0476779b9f0255f7ba0c48463c0728904f3f49da65745ba82c6d43a1f69fe0a3939a9dbd0a1a326c8f122467f7a474a1f34db3eae662385e671c45d9d764ee7480f9951044ba58d7202bc749b80f7560df0636cb567dde48d4a40e05b06a02492da43408c7eee67e830692bd56dfa83f2a83925fab3341fd88abc0bc5c912ffd3e989a4809410bc7a88a586a81bf6b7790acd86b4afb10c88cbadcde886424d5bea51773cdd5e3e5cd4b48e61f50fff2d9633fb0ab1dd76df3de572066b9659b18c94c682993a1b63577a8591032e346201c08c821c1515ca4521bdbaf1be924092eadeeb685fec9b2e94e5e7a8129068000c508d07d1059ecb2c44cbc0b5c35e209d04ebde92d82388a6a40bfa63e6d9e185c09857aa0d56047e118771a7801f72d31d91b2a9213d9211a49b49273ce519ac51236030d485f42f08ac720bc1c29c97648784d183cd4cba06b51f55eff0bed608b9a9c05f21e9c6243ef9f294fb23b41028bb9e41fc3e9564916659b5f890ef7a3e49f22981e008a83210566c9a1b6d29a341a12d947601bbbe20e8a2ff2f4bfbb0b7856b4a330eeea7e24643a853884ffeaa0c104d27a4b31f9082506eccfa428d821731e2e62f7af51165e0665b07e26048e6fcbe046bded2bf5d295b29d3b59f119d4b16a8aaa926fa044a9c4caf678041708cf383e5c494f9285471a32e0ec1eb9d363305d9b6645f719d6209d1a7cbb8facb0e40f305ba618c4d36a07289a0e44c957ae861fca29399d576c3a35c1e587381078a7b1ef99155dfd7f981d352b88ccb039d344f5d04f0cec3e14906aa1fed6c5a05853ea70c1dd06aa50266b8f687478e1e2e68a029024fa4a8072a586e336f9b47b947d7594c284a42daa1b2da26091c61f80cb82f7f22b3aaeff341999d1b8af2dc496bb674de530041925e34b039a70a5bd4df33e99cafed534296c8bfc593cd48eaa757e57fb8483e42d98335571272fd84eb0bdd4dc77e4e78d631b7b61c769809bbaba76bc6d5f38884569b2fa542b71054a3f4e5ae1808e8022c1f02024c6144a890b9c37aca0905745d2cd0ac292cf0f91dc20e81dea89a8be98bdde3e2898c02e52fcf8d6193b9a3c7ce2b311a4e764e3980a902f016a1a4fb41267ddc2966f82b8324f967f0b2a3cdca7835491360c23deeae74171489e78ca496d04235576266b834e5971b9fcf2c2cba6bd4803caa4675d6efe7811a90233012d1078c5f7f037e403770ab5d7e4f3171f299547cc45ad7a02c453314f5f2cc40519895f0b4d9bd6bfb70d6a69609979435c41d7dc816d9a8ef9b1ae1ac7f27a281cc93ab680cb6ccb9bef638381308afb37be762f2da5229025f2746abc1dd54732bd8b831994c669ec07b3110ec7cbc00753ed4e40fd0c7284842092aa1636963cad046c97ebc5dd3d732dc94a30b3c3ebcfe9bdf40e550cbd1d229b1907d49832d5d321903db8037743da1ac98e6357f8b65d1add1c2c37573c5ee8843fdac1a938565609f3bd5a76c92bb8dd1418fa12f74048bf1495007a60b1f015a469bbc47bfabd1fe65e6c67459a6a1aa218b747b52c3bf8b51b582332f2004875c7a0357a87b0a04ad01678b107f9a2b9f3039801e7d204462df86096aef837f6d71f9ab6a08d17e91c05de652507879b27e7dc6870c2618b4227aa50f2c3efb8c1568610e179bf578c13a162e54a59cff0e08157198b56957a89e421e9dfba696b3f42ed0b9ebf0cc70608463cc4c376120087bfae5900a5d3c9ddb097aad563b5af2f6abd112323b349d89702c313b0e4002df7a3664c188821c8c7f0571007570c6215c9bd603c53435a77da0b228642d30eb6e84eeb3d0e4564764544edafa987a78def852d72bf838b244a29c1065bf24afdf69f683ea11c7127efd9635a1aa05cc6970f9d312287950a6c4d2117cc2ae844642ffe1d2b004cdf93f2da62081f3957be5995dcd97b590a997b9a093f64db2f861659dbc80c8587044db8247ed16db8eda9bc32fe068d20790725412c66ab95ea879a373a607e9cdb5a62a693c01f7a0a4031ad8de040d28ca5448cc9982fe7304cdf6c6deb9b4782b3cdff0f8cdc00ef05bf3c6ed3258eb90b01e6ab32652c35f8133223755d7ebd73c24989e225aaa168afcde29289b4771596e885005b3d6d65c73a5509202b68f96fedfec2121326488ef60b101e8e60d149d69ede2b08782be1c7aaa9e0c5255dfcab36e5459a25d8f447f868ebfc9ce939471fc807b77eb5a00c653331872c774fb68c212c343a8bd3fc9bc52f2ecbbde2fb2ea9605c0fe6a7dc02b5cdd0f84974cade1832939d2dc0f383626e1bcc3d9193d96d8fec338617ae0c5ad75466d9050a4a7730dd61a87ca62a2143926d7ae8cef2333268d2274155e297d499f7730685c2fd9c863b97c7a3dd01fc18fc3bf42354fe6246d6b78987dcfbd06269081f8975c515d351b1cb8cbfa6c43925d51666a279ce13bc79b6136f195cddc998a18d1f0ad5c9997dbf8c0643280a775a829e7138b5051207e1b2e57f680f82cb3a3484bdf7626dd3267bde824c3ee7995f6f8cff35e71de6c612a06dbe344fc8745adee861dd7959c2b5b3aed5fbc22e65bba308932dc5ee90071ad7b920c5698683fbe7de1e980303df957881b54f1b750654ea3f00aad73efd82b6084dbc646f38b2ac19269d213a0444584e7bd49a30f011f5a2f6bc158c4ffb52aba4e34b212c01368e2ceb565df8cae2dd4b2e1a30cd06e019807104d08238b53ad30e231c4f8d1440e14c21c842ff709648e78b3ea80a29f726113a19918f1f74e39f5052d63d98e66005341688f57ec438b7c6cb90d21a2a86e695ffd48f242e6f642b55194a40fbafd0198f8914c6fe5930fd152d0286ae1bd23848e27fe3446836b2d76b960caa119421fe7f4e7df092e00a7019cd58301882e878600a3970c970c33118d43d13c33af57ddb66085dbf02a9ef4d09b5acd5c6e9eb48fa140713b7987ede4fff7ee835263d99db9df706abad165ff5c56debeb5791cd5b0bb1e0477d139c24d66944093d2a5743fd72b8960b4d602836c536a942ca45a2fbd29ab9d8bd7ae7d65e64f292f26fc96d7088909420db9b303ac963c427a4d05681c4006afb48aac617368da5661c5278a30cfa002268cb3195505b2ace8c8cae1f1fd5b34186798f56ecababaf0b790781e04d08eddd1da8af98950ced6dfee3caa49a78b16ca7049ae0e378da7c9ccd216a06f98cec591879d8ef1ce2ea2d1fd01f55eb92f606a70baf40c999583829a895d74645255202e2daaa498c77f193c06e563a9b3561db8983448cd6d0c9d06a148ceb58082e362864290f38139c148c3d802a3aff13a2e9b9cd5486d89f68478ad22eb6c68f199726e4788cfc2d3ef9d4ede892a99989b5d06b31bc9b76b9374e8ba0b46723ca16bc42097b1666cb762af7d04d78547d7cc036bc211a3ac9b1ea706a2946730b67b50a34fcd135bde01515004b70a7c5c7b588bddb0984b2ba516add041585d78328cf8bc39d081727dd794b7460002fe136f0f2c4005d70a0ea40f11a74857ea18b78dc79a04552d7929b1a4a279aa6ff8cfaf1dbed8c2417f4f86adb54707366df2185f97e5b58ef83bbfa513f0dc53da5cda1b83d8449e3c7868b9a194f962a99b567743ed416fc4eeb390b729ce72db98b7e37690a4ef0578aad7d3975d57e4c982f74e2f1c7902096d2fb31f58512acbfbd480dce54b1d7b5ea1b2493d610f03d3bff2153dbf2a2a4887eb6bd335192cfaa6c3f48a6a83044dc24ca5c3324197bb0ef6c126e4ef9129b6113758c075a0e9afca077e8c92940836b71c1ab34f35a4345b96f437d735be8aef40f202f281e6bc178c65b38aa65da2e8ca183d14aef4c86cc88cb6d506882cbde320ae5a77547f52ef997c3a40f173464e1060380e5a4900e29d081cc34191945c1a7dc69a40e55ede68636c6673752e3adf9e5a217384dd240af5d1cf28b37c7559b964d41ecbdb6e70b9f209fe818c9cbee404298e4863ce600dab3716abf797ad951fa831df1a85a3752e31ec7a1cc1115732e7d537990ba7ac238d14862dc7c256d21e1af90009479a93c28c86d3077931118be2d5bd79640187ac12722e0da3e0582e86efa6e2f5f7b463049311fa0af24b043167b4ac57cc6d0e4058d5e46d7533be851c397c646fa0cde2220235c305ca8729affb67ef3dc3c5a7a1e0dc58e2cc5ed92e4da36072cd55244ea5a2ca061ffb1ecc83b0953fc527c2321acc14df92f9f650c581152315728da425b6e117a3f089897482a5394d363a9943e1160bf113652a510381e9c194bf52fb1986a098e17d8b8594a923b2f7beaa095ecb50ae11d94591f4f4e2332dd85695e588d1fa66611c7cc47202605027fb2512f587355f78e193332ff6b98af8b48a30ed56b498c2676e06921ec6bd322d00064e5d2417fe5f7af871773a80ce8234b5a2b178be151bc79776191bab61a480874edd3740945fc8da656cbfd22d5bae2ca28cedc420876445dc02d3d5d987e7389a972ae7a8aad24c77e97bda20aee0ef68b1014afc1533422728108eb6c21417784ff97bf88493404aa3d485d4d738ccbf5f99188b4723b13739ca1b158341cc62a222e1e5e7fa225ad60dd8152ae44975642622aa4ed6c5615bd309d4e2c793dcfe5a089b58045307fef21892b76244db2e788693899e21348de1c7262ad54a6e9acc2308b71457b725507f175da4497be1b835d3f483eff576d8ec13aa3c57b7b11a0afd8a477b270813eea4600f6c919162e2aa2764f13335b917e13e85f71d4b5aa672389caba88cb547cc81e7cc8c31e88a64038534027af918723e9ae5e7ec32c5b68b8fe6a803b19775648faaeba5d36e7486b9c68041485e8598701748201b31e8245bddb180b17e2230954cef73d3815afabf991dfab9071fadbdfb2b153f75ce519f8f7b479e752790c64d18f32ec7ca29359c994a75206a5c23d5f341d784c1dd6bb6df3caa4ec39dee34e8b65dab6cf22e599bd08b72f686a91807284a7976ca613b5f3969ae4236027b0cc2f34018bb0485bf2222b5ee8cd9b74d60b9e4923e151e29f208268ca9b22514e8e2f8cdd1f1957d68056def464f63fb35836688bb3c444afea76c355639f3b43c0f14832b5313892bcd073da802b1c58433aff0d6d8712e375f6f9c6defc9a6d7d26e66d066809924ab0fc3ad227af43acd3f67fb9a2aab0c2a0a6399154bc988a3fe4983e841b9c79d89ad26c6a3abf912b43c16c58e23d84798856d5e802c4fadaabd41b7084d2d20a7e8a775b5b41f27f9fa7318129957b4e3b6261812c0db74f8ab21f9a810f2c2f979147c31d310b502893ea9bf3e62ad2bd6b088ae4cd3d268883918ff230a958cfb6009fde57defa7745d156795063b92a27aa598efd559db04d336414fcf059876b389ae2a9843e91f22b181dfb2c31f76fd3acdd15d3050ea7e91d4e2978899d0cb53689f687f2a2eef9191e55de8d397b21f9dcbabc170b5cf930b192780b07cbe27720dc2cb66773dd467b7715cb278beb1e3c7392fa3801248a1e848ae3136a371a172d6db3c5e4221492b011c336c47654d0923448d1905661944d58d83fcf968e6b7e09d28f957f6a7ab52b7554d938440a4107a272186f4679d31b1c1d8656f07cb452a2fec1abbd28a3efdb5bc25945563958d6f38faab7dbc0a54b44ba71db2169316f1642f111e69aa0d7126851f264a85c0451f799d8ab080b3528cdd9c1ebebd987e2d660f845313faf4e493b74d93b9839a556bfdbbc2346906e766a5465a8307f232ac0040a56dc969abc2e7519c86815a6d2481944d77175f8f8f6f666f82504f99b5a3368884d07cddc4c92e81ba68ec91ad05f3b9ed1aae4506e3974018c95665ad249f21c6882048854973c71090afb6f8222df5175952b9eaed98f90886b43d36bae4bfc9c8060786309a5cb1c5a47ac26e78f9cabdd84d870041ed102553e8331c06e81fc6646c851d60fbfc4f4df5be47347ee07d3257f923b02331ef9602c566bc2e008b6bc48d9586ce9cf9d3adde5e67cda07655822e00f66b95c32676e22d0ba262d04045a3e6cfa9e2336af3301cc5e23def47e595355271d08650389c4dc7cc25993a84c62b6a95cc700f0eb83231af1bafca03da774c0d8cd4531ee4e3e4a5ef11a5c4819eac5a00fe960da5e4f50c5a9bfc92ad7686ea7d652c3ea9643bfb829bc2932c255b87171d9d3198ed794cb2887291913ae84bc27b5142ca6a2de9eb3a302ff3d8f817d8d7dc2bc9f6bca071599446cfc78cdb58837119f200d44cd16160b4afed472139c34ab8d8f76df4070bf1c520019de28f486e5516fcbb9af1a1d015987fbceae835c0fc165e35b73b9d9608963c0cdd0808cf7ff680ca3bffd60ecdc63ad7e345263253c01df836540bba7319dafcef0f63431899f1c7f58ea4b37ea3d8d4e9a5698c5437a391220707e34af9017e37867bc1ec766c6a24108ef4dbf67130d23eec4f3299a127be649f5d48b478a83d8c81ab29e91e1157327a3f6d2f36957ee21a1f6207a586f3d5a463974967c13e5a219f6d3d3e688587eb8b4d8d914cbf341378d0f25c681026c39fe65403d8d78c764458334141c46098a899d565ab8c8ab1a7464039f72beeb4f0d3b43a69ee063f31881917167fe6e884b681bfe7cc8c03f916b0bbb345ac36c1fc924decfa8ad613bcb3579fa1515ad96495625e259119149aecae93661d32db0d26b051a1cec2e16d984373c5b83ce79b28bb431097894ce01537dbb30c4ad642b7e817b4d4736321504967b70f5c4c8e74fb9c483822668362650dc143325123ce4701502e550fc9c5141534b23240e092287ced7dacac60cd97c01f2d71516c22845b39a943b11264a4f98ac5864ed4e05da850f9336eaa8aab668bce4beef982358d24edb6639d6baeb68c1c37534dcadec3666b18c8440f6eda6cd3045b7f068c193be24b54372a97391a3f143f4b96fbe4a19acb20694c3bc69c2814abe2ffac3514ab6e7371c22025fee43add653981c9fa37af6bb30f27821e4dabdd6d847dff803decc77febda719cdbc1abd4f1002b81db9eb3494f20c1c561100d6babf1e29127a681474a8deb6c44db25bd82fbe9565a1c53918545e07c12e1e9cde07243d5d6e0336134d216dd5b29709cab0057549133fb6a9e95fcdbebe08eeaa5486275870c09a205ca5c32eb7322b20b9bb81a2064ad4d9c8f14c723c97968e319b844a47e81944fcb682cd8edf1d597a4b98a3aeeb029249772b9bb57d5156acb1d1036b3ed700c9495ea285ae5a82d5c788b172eee610a6a1cddb31d9c27a77614b8939f9e014b017580cd6857531bb6d1099e2f171e4a52c3d119addb6816cef1a05f4ec5d9a662dc3d3147effe9391cfe59bda97baa89abd69ad986edf4eaed759ec0913972558a840a9b4fb87dfc5148d26340b61039c1741f87a5e4f858f03c316ac6d5a24e9e42841410adf128d25503a9c3334086a1263eed865c50b7336fdd52f76d72e2a07fec314b353af032bffea9d6a8b74a7b1d3a943c5ffb733dee16bbc179f5192264b5fcd5424a10c3c3e2fc281cca8084b9572c48c8d93e312e3806dd6e7652eb6d2a2a615c34fd018876287aefa6d40f06d321f0b0fa476f31f327a2d80e28162b14e49059b8e6b77577778f61cd60e57099e2629bfda09b44aaa624e9e85e9ce8f2bc9a8b85db28ad19c890f630ca358e4882a1485b0564ccde5028573bb92180ee736ad0fb66e5d02785564c817845bb4dca29444fd7a3b162bfd3bc01b658df1eb35bc8aaa13dd7eff92d4c66a7bebffbae5e7b52331631c0eac829d3b7722949011fa7398d973cf1e6bd863851452a7663ab3da869f6daea5b15973226a4f23daab4d66235078366a3532a1b4285c93c2d072bf1e9ad9b256a3e4bebf2ce55b46c885e78a6c9342e5cb19307b51b5428633300aaf7949570fa37aec730fb0d8e7aea7bfee1d54e13370c9da2e51d7a81c67556a9221175a975dca9728668573e1bf6f519554def0d12cb3a1495f36320cdeacf75cd9f6321c963e372b5b65e7650f158488dfbbd40dcd438d824fdd1ef62318614b64480a822c21197080d90be39520d1b5b59ca1f555d683174371776d7ee08a99679ef2f2a55af7d4e77e08b701ea0c9faf28ebb801067cbc708e951f2621385762f00df797e802f14366ed9aabbed3b5ee3ec0b2b74b566803619a6282de0bb0dbfa16faa4113f90d317ef331f2be8baebc5cc7f301068ed1e7a32f514948c9219618797004b89b4aace04714072ab0e5d57f0a9d06bdd6211811eaa2bbbf76d4ee6200a8afab8c5dd92d331c123b8839cf39cc7cf457f0bf04e35928533145c0a39c562b52f0fdf8abcbffb8bfeb8215fa58f797c0348c8868c876552aa3894eb34807bc62accd2b3f30758b081298bc4c3d4c9ff927efedb828bc629ef622e2edbe178ad70532d081833ab8686f4b5a3e2f6046725d33adc021060962a6be6e410d6e651f75ae03b870a609200c11a549233164ee4b5782c1c6c88ecf8d7a96ef581a4a0c0580cc5c441962ba84ebe9a415f159aef0f78574ec2fb5f0ab295b792b89aeac2df77b3c743c62584b9a119afe81d960c43711436eda67435288f1581727586a29d9c1c98ea945c64626aa7122fc420dc9ecc80d5e36fc8d71f8311b25acd3d11d7bedfa85796d4f02a5e9476845c8806ceb18900902fba4cb1caf338330e465c966120258486c4b3e0aa32b78e88e9502b7da265e804f1f881f81fd948209ca06554af60b631991cf8225759d84d453be06e479a0f8d13186ab504e4de94986f469b97fee6cccbbb54e68336177fdbe678ba93b5bb503516474cb98e4827d20b60266439bcc65b70ed829b45b05b0d7d60da7b099dfdc3cfe5236794c2365e1f655c5e7eb736f761eaa671371687f99e213616a0445dc53ec49144a26cf8041a9ae74772089090fb27e0349b5bce7cb9d9f054e9d5e72a8d1d4581edfecca0dbbdaa3a1cc8fee8b2fb90e25467ec448fa6df4b05fdfd427825fba29d4c718203593f9259c0c593c71a00b901ab3675402f096a748b2d6d66d3b642a9c9f12691108578ea4360001376315c56b99a87f1f7d9401537052532e5b4ba0778aa98f8fe599c4aee56f9e5b882d64e33f101488e10f5502ddeb7f0bd6cdcc6be6c72412ba4e28920bd20cf3d72b805c76b6bff2895bd13150b163587a152c1e1664ec2db9956a87ddbd4471a3c1bb83583b2f93ab7e8eaae739663e45b2354d127346c08191f006b1e67d1b5a7f6714c5abc4bc3a22c5fa11d892a7f1d5fd12655145478c42216c66401172a196bf689181cf37439f738cdf0cbd738b03b7be21966a8a95626eef8de15ed1c6490392090eecfe9930c247eec4ef964d4686ba58a401dd97d1ec2a23b02919a8772acad453c899cbfb88182df74c91b6cdb5265b96b2f4c7ef9ab0635f57803b2b5dbea2d3592c70b2c4fa88a9127c780845d0e7d930565dff12e9eee8094dec44d0b7af5b4e841d5f1da0be424993eac8d405159c1168fb98fef0928ef4a94806661d37e16417de158ce9f52282af51791e78e8c2e0e935fe5151b508a612720eeabe36bfdf7c04c34bf1bd35331088697e53e6b554dc080a1739d55faec48ab7430901c5a37b5c61ab189fe3deb0f2c781d231eb57dfe457b280a2aaac141a27841a4e43973050538062513f15edf39deccb4d6c87791f0475092bd3c5e91384984a0ebedc37daf8e79c727c2f8bf8ef9a79a0c8144dfce77336ad85750e54e9e71bd7cba0ed50866c1d3bedeff85f7b1262be6d8cb2045ce905861dc5fb3b5a113b4253dcff5e9b44f94bbf79af7c82fcf096fe1ccc48a6b96cd5e7fdd3796b87f14c0a55f05e04cf890fbfd4a6388e54d97c622dcd7dd84dd9c1ca288a3fc1654f7d33d192b464c9ed3edb1610bbc7ec142f861c925ba05dbb3e1808320a66553ea320368ec8bdc5f757aa890825673e9d0e80b6b60a0eefad5bdcee3c03ee70db608d24f5e0eb1c64b6ba92f10b58be246655b984b58bf5268504801b4c1cd40c633503d87b907452c332ea16d7be122fdfcf5ffc2fbc950afe5c65f9fad7db166ea7dfe250aef83f2a4da341aead9f40d780fa4562e3e06a47a1b271027add92ffd595638952d101332245d0b94d1ca6bd726d29ae1b495f01868fb79e8505e1caeb7f8379e689add14e2f5efbc750688f4b2f76245efb8addd2458a7957f3829f87138e61370cde7ee72eeee9a0e76cfa79e6d869c3f2e33ad0ef7f6945af98c8659c936ad10c9f66a639c33965a25eb68289e473243c4a8b58132eeecf7fca073b341c6f8e51d88e955ea4b59c5993d57c0d52929d03d7d24504aa6a8865d3766ca9fb619bceac8214c4c6b9bf464493c534c5eef688bcf9538860cf14fc83c4471baf632eb4f8f7ed163fe80966de5f5e7598c495a0d62849ff611a6f308aa6e787752dafe0ea3df7826488ef9040d0b4f05c199ef9e7e2bc39740830cec27e59925a2922f89ebe65089eca31c1f366e300cbc6c239871d7df51975b91a4330850dbde552ef715793df19e8875eb8b9427ee44527a2fb8da0aa347d4305549866d16ff7c608e63781d80e5ab607ff8472bc9d315b8ec6ef1d3e027b9a177a3ff565d21eaa9931aec272136a3c70dbd2335b1bc735f93fc29dd3bba5c5d32f4868f9296436d057756530db0a65b632ed0cb64da888454c5322db6aea62ab1441c8c21bf406782e5b95ba75b6d8e2ceb6cd6c23b9a75de6936a6c3afaddddb0309ea7713a5be55522aacfbf7e07cb5f2e45081f5bf4869fcad36f09a4cd2643a0145972276f7b017c3ea656cb3263c73da1c1ea1c3908ace4a8a0f630a73f0e0fb8db2de4b85c650d9c36edc0d893a0f0bd3c354bba131bafeb8898ba1ec24974d0d5d75b82dcb02fb2f29359fb37d57fed7a9114bdde0e4ece9ee73d4317faef53babfe386399555e41f7dbb19f1b1d076a4373506414e254fd6f2985026ede5774425f82749765337f9cbb20d034778484e0f99cb08abffda6a779adf6097c61ec22ed7a2c1d028ecebf4894db684804ff822fd4efbcdaf5dbf276061faca150e8b9947bb7bd849c10fa8181551706b21421f5acda536144c6b8daf2d3e7ec8c3b5b2b858915b7fae0e188e7df5e6e90d5bdc8cfce90987e01b2bd945c8b2770beca31eb77f393bd6a334ac7a40eaae47e0a675036e591c873fd2b8a78332faead69673db15307e7e23b6eaa55b444571fd402d873e445983c501e9638039fc8c5a7368e7aac86c75fc0396a559e563e733fab6301059931aaed3d97a22000c30ea9323a9d7f000de74d1e578204ccd73f26762def3a8edfff9b5c631ae52a37f4a96346be9ffc2f2da8a9a2ac20fa7509d005a633fd76b7512194a4b8a5076b2f01809ee0543678015475ee4144772b8d2b920f52eeafccd2a7aa8e9b6a6584fe2ceb571abb8f0c9ff0117e6a3e322024718ff2b73f501c4520f1a43820e8b3ee29332275ef68aa602da6a5cc127870a773217642d56cfa1e90e433ce8f30c173f20893c005cae2e2c6e1985974cedd7ef6c07622a0a1d92bf6c1ac7cca43a0c25aeee2f17e3f99d275c6cf7c510bd34cc6a20f0273e3a89a98ebb23322d7fd1ef2c6bd3fc8e4b4e6b130b38c7b343009750608bda2ceef3f764a28fa2f00576b41d2e79d34ed312fbc5351df796f79e7625066cff9a4b1b739df8cacfa7df141ee7086b64dc342808b77f4bfa4f5f20aa1726d085cabe5c2a76a6c000118ea867928e07418600087b0c7a8765a8cf0aa1c844ca5adf6a109cbc8c33502a03b2e339a87473862c09b749af5e83bae5eb411f33b3558072891bac0ab41a2ca027ec82c00d71bab18a2f0b4c53bac438b48f6ed0084516e21bc9497adae3415d180c25b6e9286e58f8697cb83540b3f3cea1ab20159d3c5b0d4f69b888562096639e4483971c5d46631939de3320892ad2e28f567d84cb7127cb2ac86e2cbf80dfbce06b8df580d28c574a4eb82f5de6778cdd8fbc7bb79c4a3bac618c0b0abb1ead50221572d9a60333a26".to_string(), + genesis_string: "Concordium Testnet Version 5".to_string(), + }, + timestamp: 1709028205, + id_cred_sec_hex: "170cd1c77e6f422f6591dd216a4658617908469d22e0b11db185385dd0962114" + .to_string(), + }; + let res_json = identity_recovery_request_json(params.clone()).unwrap(); + + // Verify constructed request. This unfortunately requires quite a bit of messy back and forth translation via JSON. + let res = serde_json::from_str::(&res_json).unwrap(); + let request_json = serde_json::to_string(&res.value.value).unwrap(); + let request = serde_json::from_str::>(&request_json).unwrap(); + let ip_info_json = serde_json::to_string(¶ms.ip_info).unwrap(); + let ip_info = serde_json::from_str::>(&ip_info_json).unwrap(); + let context_json = serde_json::to_string(¶ms.global_context).unwrap(); + let context = serde_json::from_str(&context_json).unwrap(); + assert!(validate_id_recovery_request(&ip_info, &context, &request)); +} + +/* Account credential creation and serialization tests */ + +#[test] +fn account_credential_deployment_concordium_ip() { + let res = account_credential(AccountCredentialParameters { + ip_info: IdentityProviderInfo { + identity: 0, + description: Description { + name: "Concordium testnet IP".to_string(), + url: "".to_string(), + description: "Concordium testnet identity provider".to_string(), + }, + verify_key_hex: "97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80000001e90ba8909d0806c237daf3c4d533ee12c3c279fa9dba217207c14ce949b67ec11723e1768b4c8d8d4300a4e7e2eed8abcb0862bdb11f5ad4a28d0a3fa0e85b689a994c417f5a306a3050370052b4a56aff95349a2c97f749a68085c77acbf1df399d3603227f82698010dee3cecb6855f27edc0fbc9d183a909835bf53a79ad6771aad9950d101940db4012776a06b44596134ae37e01aea98f2333083d4847e8dbf585c835e1d24072681933093fd152f8524eddc85554e1f5f5d463ed3f4c06b5d11c1d79124ab6f89cc556f44659e46590936941a461941e934d26354ef1c1dc7ca53175305b19adcf274a7493dccba69bd98ade4ea154f028f96d73bce9f9dbae14f6cc44e164d0cf4ebc850daf28f0514d8d11526495277066982c33d13dad43482806148cb2c8015637540ca0649c22de2fc3e11bbb015ce2df7ae3cd845b3eed46c50ba4beeaf9afb7834be1138eff0f0d2c991b33e4fd9eda1605f22b2ec492fbdc3ea4f88d66e837f044a10f3abd1edc6f54087cf2589215bafaf4e487d280b2b0e1bb4452dcfa5888a2f8eecd243180ceac2eba4f24b79c2c56c93459a8ee94b4e69a0ccea43def0ef34628acb418315867594ed1179d4e349ddc2b2b4e44f016a93152fe07bb87b7c2737db33b1974cc1559245e96df657abd22bcab4673a711974eebe760032096ee79865bb6b5abaaca2661044ef058aee26ce1cb79bdcdfeeb0b6ea47e731957b5b71ca9b30fcfcf84317351ba9a520f62695b688fc2125c693125c3f07d82536feb477d721eb6ee30eefdaee074434a1de3e7a40b03acf9b42fe1879e7b1411990a08227889821acf06b88c7e086028f1af2adcbaa74eb8936f665b3fd011e08ba7dd888ec898f10114b4659610d1409a64112ee972b7b1f089c1c9cc3be74e6b08a761ee18d0bdd063c4b6327123e0bf9e8dad20dc8fb048e06caf87adf3fda5fabbe8398394051765b7194b28b74280f9d7736a2fdcae133cc29f0466c6edc06df087ed29ab703dd598183b75bbed6b0a71947fe8a18fdcddcd49386558911b1c1362e5b8159ae39192e791e7254321545595abd9ad7e859e80ae3dddb14731a981599f8e7fef5bb77151850fe343f7dd91db35cd00c1d02e7101c5dbfee9d9562e9200ce13e7638a5a92a723299986d22e9699a90c15f26151417ad770c8534a1b54f29dd3b64dc2a8f02374337344e5aca3d292364ee245476def3fb704f8bc065a7a1a6a79ae2387bfb80871460d9f3052e85f7ce042c104325800433728da6f84790b049df5933a59b3c72485bda556b3360efc0d02c91abf672284bf7c77d0638667e13a3dbe17d2793395009983fba2902e7862de908d22f5562eee6b5f7cc59f10a1afa934a3510f4b032f136c78a56dc5d8c1132008808473be726b698a91c0030468b64366026ed779d1a0e9d65508ac22f5800406f084c7fb4b04ab1c4af3dfadb9e51cd7650fcdac64ac2f8d8f8f9e483e8c44b9fa93afcf36c2210110cf04b3f154069abc50df6d8bf6a59da226fc7b329b5fbc921ec66d87159665b9b25ca49712f9067a8dd901342d97aad36a0129336b9a0e51d1174844cd719163266c8b30400886600e9ee9804c4813b4f03072f0a4dd57915f15ef4d8208e609b0d08126f6aa95b97096c96a0b125f097874dd2fb8428b930211575f316cabad5f14432b57d7342f883b120bf2a134c36d45960a9a9a68b8fac820f03e209d48197d467c416fbfd764868c63770a038333542835ad8db288a9f99389e455fe1ccb74a53b00a6665bfc206a9da0c15fb8b588f504ec9675bbd75a223f358545b8d93f31b6be902408000476ed980e7a5e9b13046353342e3081dba9ae0af25e6ac3e0130db1714a2fba6fab008513b4ac1a285c10274cbadab17eb6fa27125f5c3c3752a8ae7f39cd943a7dfa7ebb181cd300a3dc6a69d11591b91d28eeeca9891035693e0b71472015cd1754deed74de61c04982153417c3cd0b060632c8faf7c3dfd6591867dae68923f9a5f3d8190000001e93caae1dc016ba6b674e97ec58366e5d9bbc91020c734f15b9bd9454a53f50d081f4301d80c09b30a0515111a1089db200e59fe3377513e0f2a07d7059854854b9d7ad2febc90154d4191f235e1db4e3fa0690312346c407a960b18723ac568f8ef3b09c3d8574fe3a118815ff80391b71145f822cec0921006a3e2e248e75f4077a5899eb5c94594ecf5d53c9461e5d0697fb336d8463c9c7cafbb5d2f59f4dfe3fa3e93cf1af6bfef4b862e2b3bf6a29f456025829b491b393fea7bb7859e4ab776ecf95abd7fa1e4e03c77525af58d297be183a9437710f72b35d7fe18c9daae5a0b0dedec5d91eaa1cf779044bd304e9b5c3ab9ee20d197a057a328a71326e9301df60c729ac23f4280245c262de278cbd64c9c58e9591044943c20dc51f8c072f4121098bb8949ad7b8b042b98dabadfdee95a825f106690a736f46d518a7db5ba13f5dc6a3fe96eecb5c9f138919ab329cca7582e448db8d51fc30528efde67a4de4ceb1df40d4c3248bf1bb6618a0411dc205e67f192727a82784cd77839b3f91e1c0b96858187c931d78a79ea1e8e63924171d440eb117d9e5696dd9182a24d66c1fc885171901016f92a23a136b07b8033cb163821ea2ceb6917f36a17fde1bf17fd299f0bcc3021f012c6bbcff17df0e6bca3a0aaee4f298161ef78a6de30c0b14bf3623d8fb980e02610d31ec22ce6449f19df75acac7671f17ca75529c118215f3a7ed450ec71e7175c013de7a423cd96425d10024b6470b7b3e90ffd8ed12a19bd0dc61be354fbdfa2b6f42cf7fc427eedff7d90d432d3fa2fca09a13d0b855f0824c081b8f3010e53deabfc255ff6be14e82778e8aafe91eb222668b6352a0761e424e16675b95cd15023ee7a1927e61540c19f32177a7b564834df3ba08f6015b85f3abfd3589a88a6963771f7449137d8d58a98ab1411abdb23196352eca8d820cdf41a9bf4690e8dc37e683a5739d82a933d1295797ea30d0cf5efca3a26fa1eb4c477830ba93d9069eb7477419ab1defc6d57deee580bd707388f971e9a6359ed03b6bbed63ac29f765266ccf6f74631b60088c4d9415eb759611ea07df121917596067e74558180c9d7f77cbb571a29bdc892a1d8431b195e3c8cce609a221a2fea32cf948d5c0aeb37e3a1f1e01668832b1e6dc21435926f28037c079b721115556c5eafe1da66ca9ccb63233fa3e2bd0fbb4718b2d8a904f05a775d60f9f6e7c2891078883306208ebc8bab4c61961707bff16b1ba070e115dd068837591b1d689547e80ea710a954ff5f8a0db3f6fcc06c50e47df20e837cf793f67bc8dd66a060e0030c93b5e1190e7e934e2f85cdc06fbf5728ba85b82aa80eac192d900a4f4881352756e3a86dc9164c82aec566d7dfcb7d6f61d6a6cbf5959b2dc0b5eea15874c9d7c619e227f94fcb235ab786bbab5680500615136434e52cec54cf0568cc57e8191e9379607878e8925e5774dc3b6ecd36b7b1ce6061719fc4649c4457b8a73b1de11df3f475d7d763c6e31e6f1311273f3dd007b5c36ae9641bc1c1287748f9f27a0a3728c90e2ee092c747b96fe49f2e4b438d14dad21cfee9ef30c4d29365ea7c045bd4e1ead7d9a78525d33c9d3ffe81b81016349ab583633a3343f73a56da136996d1fab654f3f8143ce854367a9958e7c7744bb6d3ec0f644dc11aa2e9b91f04e70d89202fc66ed4567e583e0729dd453ce8284c9b077864f2596cbae38c455c492ff8353df4db5891e18ac5fb1825ada5b80e7ffd90ced8b9395149e5d96731438ccaeee7bfd05cce5c9e220f96eef7e15eed4b5665f94ec7e6b22e24bbdc0509be51a5128f5644264641a88a8949038d6bffc860902874d29495ac9c11dae076eda76c541937d07b51f5b56623f6918d4b11dc2751b964062014827cb8480d82bc7802b29b3c86aab59e0ad77c9d1c265bca0c63d098d984f20684ec5897023e6c19cf06a5cc3e5f5ae7c7e45900a6485836207a4454128e9c6eb86f151606321a68a99ad4d0e1235489d0c62ffca462e5be075656ed3bb4788238fb555cda5efe8690192fe7319472655975c5e0ea09b41b8696df8422fe2ae259d8c50709640a135f660af6338b0a18b2e948f4dd2fb355dcaf097d75cd74337c930efba9bb51d40f95424b091cbe4be6447ac28c3540b12ca3e35e076c37b02b5a1661f6e8d925413bf866e5f5316d76706614581963ae622cae906d1d68f076eebfd601668e0c87419e74419f3b0deeece71786f2915b64fc1c97779a953f7cc2770936577581e3938e6cf85f12784beea404abb446754c2bf5016a0ba7862502a587d995f08bd284ebb2f45aa07646bc6e1daefdc5047e2fea41ecc954067ed9e23902d6a8033f68c2b2232f0a9ae7db7a8dc53ef956aab7e04c2b2ffbfb0d031ffd36bf6e4c081a209485bdb755b4a1b677a515cebda98035534df3ca7f1c1127d15acde5c75b77f2cf218df18b8566d75b30488179b18756839b2c88f4d3dd84ea1044abfe791c32ba2dc5d0d7569a9600c58286b3c2c2c475b9bb2138e47895139452e92a941daca3f2bcaa8c3dbd7dffa0252b3ae7421597e385ecf9a036dcdd86e6a1d817f2fc41b1c73cafdf4805f28de37e32df6e8d0b0acc4e57c976c536023961992d7201df5d47a07cd8f8a83faefa3b55443029578cb1ba9dfdddc2acf9172d141de04505c137098f9cf19e4baf82447266861182411a31a1f02a1b418b5330fa225803fd3f56e868dfb17da635f98cc18033856da8a3eefae0f6541d0517ba0f28169521e5d8764d69eda3fe76a7de8d9b45e92c5f16978b67e44ff4edcdca2a4cdef1286e2ea9fbbfb9f6c0a432b45867803ef97384272e1f71a9c11d434a6b7b7448bc64aacf483eb8b496560142ccfdfad1faccba50bd5ff12fda1704622dcbcaf8dc7b07c963b1d429d4da7f5b33903b45ff4020187581edfdb67132f85bf08b133553d708d29c3721fd867b5e170eb70e65c9cf5b344944df2e879760c7037e39383c24028e6648f59f092b5bacaceb5d282f31a3969dbeba350cb907557553e26a7f2fd204487b7a7c2099db4a6ff3e4621974985165227c087c866ab18632b425c94c8a9a297f1099923c2c6003181c0c788b3f63584e9870deef0b4a95260af3d411fa127c9ff56ce9f3d1195485e15cfba7623a21955d7f0d2e5dc2622c4cda3a23e4c9500ba9b46285a7cab36a56c01e4aa371be55c99dc14cd4d386cb65bbf82617b4c83f0574b99da242c6dc814726bdcad4708dbd76b9b42dc44325951b40cfedbc68e744b3f95ecabf0383314764aecab1416e449e00ec8f59552f19e2d689dd3c5d22565f20ed63ab97d95c5694b75858fdbd20920d20629c638eec08a76feb079d2edc1798f3e34b3d7648571dff2f8a9e653d173f196a4bb1c45987312c77b2b41b2448677953aff1671763f2767d7ac7e7a2a605934d751a6c9dbf9351eea1975cd1104504ca02656f7a8b9e3e886768bf4466b7b3b2f64ef89f8946e88b8113314aed94ae9058abb2b8e5fc211d4363b34ca026e6d9ff052ae9e21feb7b23964041bb763f6f93d5b0b0f5499a662eda86adeb0b98d2c1b8db808dedc3001e5af49abc66df3ecb1134277480097137d0f1641d7f7442c78c38079626f7a7b3f0f04e6094681ec8eaaf4e1b2a4d5ad2f5909355f588db016e831eecaef81e05f465323113da5a5f5056481a3923c77ecf237a4a18404de91a9eaace74a2dcf2d66fa1437df048784c9de024ef674e50113f18b100c48f6f4e6a2ba88ef3f8d80c6aa9d08eeeb3e198c1c483c7cfee4b275cc2af6886c85dc551b2e7063a2fe48909a37febdbd5effb4665f2b19992356f92af45121ad6ea3889ab34c4d2b56dee7147090cb7739b42291c11944868a50ea45f671c8fca418e5c396f02eb3e8082f0ebeda069f8518294211aa9e6efea55693e8646a7f214d12de55f0e5fb889f113d99c2211c18e6258e17789b5773479dbd7b905517c375f23d60951444707a5292fadb7da6f53770938decac8580c8584984106e9bc13d5c307e5a78af4e8093bcabfb5e8a2eee50023ff0ece77cad282f2fd14b505b339ab3dbc87eada2a6e6cde041ec5d5ddb32fcab2d94e1335fab55a5d14973b4d73201103b283115a1438d76265dd7a07c6cbb30cf6329d57503fdd92dbdd4efd3fa5a60c57b7d39780e6f846".to_string(), + cdi_verify_key_hex: "2e1cff3988174c379432c1fad7ccfc385c897c4477c06617262cec7193226eca".to_string(), + }, + global_context: GlobalContext { + on_chain_commitment_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a8d45e64b6f917c540eee16c970c3d4b7f3caf48a7746284878e2ace21c82ea44bf84609834625be1f309988ac523fac".to_string(), + bulletproof_generators_hex: "0000010098855a650637f2086157d32536f646b758a3c45a2f299a4dad7ea3dbd1c4cfb4ba42aca5461f8e45aab9112984572cdf8ba9381c58d98d196b1c03e149ec0c8de86098f25d23d32288c695dc7ae015f6506b1c74c218f080aaadaf25bb8f31539510c87e8fed74e784b63b88afba4953cacc94bceb060f2ad22e555cffe6f0131c027429826dd3a4358fd75a06e8f7c5878791f70384a7f3a90f4b7afa45fae6e0fa7153b840f6fc37aed121d6c51225c56d1ce6bbc88096aa3f86e6b3517daa90baffc69b9eb27aaebf87f04ed091412547ec94aa7bf0c8dd826e3cdd621e11dfef6fc667c53a5abc82c163efa51435b3be74f47073511db07c1af7229c78ec38554a3f7e9fa38499201d4fe6c80e867df75f2c540e57d2e15f9f5e18f73c0a804567494aba85653f57f5f990111d680400bdbee2d0678271ce93a119c107458b8d4e557fc870de9cf0ccce6d34a216a6ffbec9581349366706377b436261200834fb2654ad5cd34f5bfe3f3658ae94838ed38033886844008143d823f7e49db43017655c53b983a883948d7401f26ed14daef0dc46da30f57e187ca8e027063174a412cbfc8a7b606b41fcf7f75b698ce9115afe5124e8d72f4b34ce839742a46885ed60af26f24f8d10d46621d78b5772b0314311ed3bb627e93bac47e7eda5ff4d2ef5f98ef7265677a382a1f7b8f9a43d1e563b66b6de94c52c3c87169bd19b6e884c6a28dd6f51ba64bc36ac07926a8d91a64e88a4e19b4c0fe0db8b76c9c99bfcddc05fd5630c54bff85c041fea34a630ffe1e6313bf039477994696db0596f9e2522e04ffdb530147780099d918ee47031db2870c00b25ba6d201b00ef2459e73f6a8219d7a7ff96833bd1c8a3c24285d93b85a321d1b6e175f2d9ef111a7304438b5f5874f064539d27bf8bc46d8e2473d458f957fa206bc417ab5b8ca4dd5f0595e21063c1bbae14f07bec6f12d95a05166b90896f33aa941e0a057989dcc1ed77430a3e6f93ab16f62598b9816dc203db6ecc7fd7989d2763d4ef72abae3c4aa1e1b9c9965f8772a1be640a18486dc0804a2efdd7175316af3220a6c10db0ac0e2913de42c44f2f54a20687d2b22c9636e0ba8d09747f2229ee6ba0fa49c4a328f2ffcc5ff462ddca0de8b1d13cf85ad590183767a9ae4179b3c617ed776cf14abad1964d362747492547359c4b65e78f6dfb452d5a559ac0c24b8affdf13256e43d3213bdda0c056172771cda382f3f31fecb82ee8dc81a48fe74467665c4e34e07b1954cae4ee97270b94a336779d27646348ffa0eb52663aa60bd8b7fb9311d38a7fc1f0f2842081cb81b594dfcdefb44db1caeeac7527c5a48e8a8ecb83de0514ef184f2a82c4110f9f64ea655fb42218f95e27c81a0adb3fa7b441b71cb15113aef7c318d73f911688d34111928fdff181dbc014be35cc7e3e5bd75bf767686933931bab272eac577558b1314206d97611152ae82c1bd56ab92a4378dbe73e76a9fbd90154bd1a8c73c681f70298d15f1574d7038521b4e9b9cfc0d900c2d74cad594b68923607a1b913cb9484e093ab8ef2020ddbbd77d041581487f41e2dea13832e566325f4fb0f9934df94ee068c676e475251659770b93e0e32c7739d3c430db581308817ba091c56a5087c6248ee79baddc3c233e77b3fdbbd73f1a4298a8277055ee32a96b7e83f76e0070ea65c4a1784e9e281fa70fd9771c762a91c7ea014f60c87d09687768c9d0c8277d84e8af2eee505fda2967631a999619dc332d98bd45d40458923de655769aac754bf9081dbe9318b4880bd325e6be713f3aafb65d8e44b9143acdc7f2ba7f15290c84225c77111e0629d0f82f2f367d2641d0c3658fd54e538286787d379252876f71ec4a96ca72fdf8b156059ec50131317b9243eb71c5e84ab6791303ee0ebacd790bb27767701083afa616413b1d5bf0347cc4199943c938d74eda87660dd40405aedafbaf54b1e177117f632deeb6a9bd5227d9b8ba5693625ac68ed7d1604ccffe73e97a45411b99c666dd5eaa90cd021d05f12f84acf60bafe0c98e8ae213fa65c189256c79afb4b3eba970a8ad4e94374dd6f9d258b8c86335f36481affdcc9eb074f1ace01fae937a7d1a92279bdfb1081e029103e48877e4442199593c1b946aaed494885b0bd9d67fb7abc6f41c0f7574c3cc007d29a4ddc0a966270f5432a47e3e5563061b158eedef8bae522cb1f800c6d5f0a85a4e24a68a9bddb99e408d204e56cd994796bd004cdc5871798df2bf068ac48271290a2b3b1f0eba346f8746c79ce2389ed3ced67bb962fcb50876b7a32afca94216adadc0aed990dfd2b558aa5d8f35aa773e710f14f1641ff7eaa9ecab43ca3dbf32e846a90c8a3771d378aeb9277ef864377f7ca79ec7bc9e4b373ac890cbcbcf7c33df157e0cab0fcc7fc4daa2599177bd9d6bfc4be694a8e834be081bc7fccee530fbf6bac5be0cd1656b4c79ba6e121c39640cda83f81d17324da795da00f9a05317224ba827cecea6cbba1459df4a5bf6b84cd009d5864d44e747e3f30866a7c261004f861359e717c414a699461086e9136e29f9973e5b3f008a77fedb184e6835bc50934178b4bbd8bc9ecbb10865789d063ee7781fb2dc7b9b641b73fb172c3aba915c420c1c72cdc999d1fd224bf4d3d8a5ce26fa36100533682964abc2e99368402b0c518df1b6af245837eadb31a5616884c5474d25f57a228ed2bbef64d5a80e37cb20385a0283072a3b7a11b68bef323ea4804608c0eed5ea213caf6feb183558d56666f472a9abd10ddbf49650c5318109f9c5ed8fac7847644e153638e29e5f45dd00da06c9b9aab4af18eda0d0bfdc48ae42efe79d4e7196ef07c4319efdf042724d9aeb2c13b089b1fde77467c32cdf778749370e2c3b84a466bdfd1aa00ca8ac85c0eb29f1ccba6953a8bf877802ca3e3c31ceba8f9ff0a8684b3b118ab11a27182b033847baa372fb6b4c88e9177b8b131cd69f6ff5ec347db17215fb9890fab1c4d265d405ca74e249fafda004f86f2a5c3fbec7cae6afe6c507e089bb2d47a9e421512c016b99cb1cb15a3b8e75f2d8ff4866731585c686871691b5ca89a1351f6a79b159530fb5825f87131b0bcee2573d3932d223a358fcf08c8d4bae92f26d7fe35255c7486484bcb26693174ec2688abf39db5f091c8c09ad804f4ae42ca8b645200f27629ece81c6b5d94ef7646cbbc4b62a716ac6d2ad21f5a7e2297ebed15f2e085859a1002a9e7658b0d0249446ce9e769caba71536b9329a5fea3a15d52344d42d868b0d92a423fa44f8a6c4aebf50fb496c62d03598de39f1fbdb29e098c6a2e2ca59cbb5e37eb021594463a72471683117b957890f8307828168b1a8f2066a5e5f2c6822589272f259b012822b8be2b15c004a2a69b66c2f5ee5d81aa9a7bbdd44d059587bea89d021c734f62828382c9f13210185482694f8b30b46111d426927ec5f10b155f825d852fd995b9c7ff27861f7fee8dd2b654473198504cdcb53154b29f323ac1adde8510beabb8cef6d1a927f2dc844f746edeef9b256ace54dd8e3219b265ae958ced6593ea154b8188bf58b72ff05c036630b6d55141c4ac227a4d966fde1d0ff1ed9748c66127feec86e7839d77c1c56b46e29d2033a7871b4b0e3c53b3cc4ac49dc8eacd0555b338265be1234b81259b3746eae71600c16b7f4bc692f0a1d3dc2ed855c9fb11b2ff5fd9e6361a82d42bd4ba00635d974fa1214aa8da9180cb158347608e0c44bb5b77397d8b233df7be91e7047d28d18ee66472b98c78091eb95760c5b381d6050779ce65fd1508af8df81a3385f185f7941f0b7aa9bcd6023c6e4db7ac3045aee58f3b2d307a5be14e2b59d05333949d9e8dafbe15b671034b7c629c1de1bf560415cf72761d4d7abcbe3b762d9a66b1b630b75c2af751f6a5d37b43e84139aeb418e0b10dec012a32095cf7305bda79bb3853c3cb1fb3c8447ee4306923b6b693020880bb11df8c78f3411f176b2393776d0b9b3ea7eb0cecfaadbcd8112c4c4ad9960001f4d4df210860470a4c89a0fb9a07229ec3476e1221f490ff80314da398ffbefac3a376c53c0db82d6785bc3d3b0cf3ce4bb54cb9b6fdc29b01bca2894debb7de518a61cc953d4f92a9c52d427e2858db633d042ca14028ef771d9c8c62014676a30ed9d5f9f51036a823a388ed0f72321a13dad31bb28fe383e456361159490942382ed3e49f1c420968877a42b7bbd8c3c9e78723017400dd178f0f89065072d7ef5f5e9baffafb2ce55fe7b35a7865957260b1789e3fddc5bebdfc3301e79b5077a92f6048121c557099c329bb270cb90ad47818398bbdddefdfa6b9d9256b8194f51b45c69176d5d7deb6440c9a81bc5ad0c8b2f31720f071a8ddb7907fb161ed658f890540ac56c506b7b98df3a45f6fc062698bc86d3cc1150db00d702b2d1e3c8eae552eab55a7afd5a5a3be2610867cfff07545c8227dd26c502039240cf00ce2e91b7bd7ed6495980639eaf919635c82c5073acdc21aa275ba87cb7280d0c6321c6a4021b1f0ae7bce23c32238b3f486ce27434721df436446f9ddfb9c66bb1f4b2337803850b2606d8954268eaa0ae060f8a6da2a0e7cc315c1f8cdad4e11bdf5165a2d72ad34f976bfc41000afff158f31e9632b898f2e9886b8b92c81bf916861c62f07ea326e94ddc4ff9af51fc0420b0d3c57fe0d14109ec90a72de74147d8ae1eeebe6b565c4ed81ac01d49b0e6a9551e9fdb7a9e040b4bcbad61fe627983462106e1c0b59ce762ce89b4b1100444048e2fe84850db19d63522d83993067cf025ddaf796e2f11d76da5a2b6ef4a409720ac4e97f7c75973584d935ab0e723f415a5622b03ece90173ea58da495f402f5ecc33526d251da5d2eb5558cbabc1634e8aced49d7c3bfb514c20f7284e8a902855b2296ae90ffb84bd0ea96aac846b6c9dd8ce92c0f9d457ab44a381046e7cd50b4958bf4b854bff340be4d41452ae1e3e19c82b99908c0a45968e580823450ffcc67561dfcdf446a57ed8d032ca94ec3e8dc0a181b9faf478275fc353ba104d5c31cb7338cc4c2cd445ffed7025f576ae84e9b08311b77490b6111dacd7e3afa920193cded42246276cb999d5d85f594352592002efa2d34c387fa1901cc0f63c0f6ed141c7d2a48d5bed031a204cca98c4e26ab7210d4ca5ba3e20df6d1b0e1daa69216b3e1ed98f20ad9f527efe75a0e7e2067c85383b7a614945669b442e1f0d7ee07160902ba9ac292fa0e9c62bc1b0e848856aa680898c4769d965441a16cec21ffc2eff717267cf0f9d4e7139b764c72826309e8e0ce8965c6c96953c3ebedb4ab4c9b35c32b7b59f8715f7c790afe8a3c7a09965192baa75262a16650cfa1706f4cdb4d34043ad1d6a53898164d049647dba7be95b7c620c750e3146155f57ccaeb283936ba6b9fe6a6c6fc5fec11b59b2d12da76075e022a2308fea5865ab5cbc12a4898de2204bb218cb6757d1d3d380b488a22e4dca9c9b1d1e2d01e7a9e707a6c09c323973dd639c7d48f25e4b5b5e95b4394a1221484d337a032b34cf77998acf78efe6d7c8c1ca4f83970bd62c0a790f38e405827a71baf31a9c89892f6745e2d29bc13b5b2873c0581df064a96bf64eecdec5504a75c7e03369662e0ad9f6c2d38c4da79b82f62aeebb69392a78a2cf6fe5a1ea8627b77d5ec828f4d445fbf3a51987023218ce4f944f03041d01976a2780fe632238a828414364b8b98aa8d709bab9c2202b144f9edaab822c33d5d8ec5f62517567711cf25c890036700b7f39b130335b4b8d14d4e9ebdb6c4fc015027907b931fe5cd99ad87976e8181fa284a53185f1e982ae24842abf28f23de420376b205222e5067a2ec53814d659485327eec748b821d70f114fae08ba181f320717dc462c88a15370cf8acba5516c093e13102d92c681bda73f3ab192976179dea08875abdc054e389b309d62a9737e05edd489469f63497da850450e1f299aea08649a9ed1982a4f8a3b251fb8cfb4737ce650d125199d07f7acbbd50dd65ab973be9684ee248269ff200e2f36118cb0eb9104b94149f9c91ebb76f9d46ef5c11b4b78bf6331116110dbe6bebb7169d3c637839bcf89b114ed9d765cd6211405e2dbbe78a4ad608a960a051097a65dc0620a3d1143439c4e04cc6eb1453e43904dc9f9db084d52b2d251dcb1d476141b74c627fbff849eda5a0abc85416b97d9888dd3cd88d33104e8f0fe03ade560dbe4baee030af797fe68e45196a2137d79de2ced8e6c2771d6b16a74b2c1044e9d381a0107adb870af01329c413c0a29b88d92b3a3c4a117424e613704917bb2aeb343dc22d96b0831db78b63063f10527cafc6731b32d50e4b6e39f45595cabd413f62117244ffc1d69122b5b1c9bd864303c46cdaa1e76a97448ba5ed266859a744411e29d1ce4b78a037d99d337a7b2f74907b591da8103613a19bfa6cc1f87b9698912b85ddb0ad0d86569f813ed4dfd3a9edfc0803427c648ef49724149e49aaaa98279b2ea7d25e3463be263b6cf518e9bfc456e993c2a3806335f13deaecd3c052e8604a08c045175d044f7544291dd4acce9db398fef80ffd0fc75b28886903d2d39c133a1869b8429c9ec58069384d8639e6d268849bc02f863f431f574ca988b84ae59427e446891cdcbed74a72e2b60601ba707b2c839d85b33320a32c1b1f7514510f70d08c474a7b6155a0ed004a23fee27c6aad072c0c751474948a1397ced8f04c5b50b4be2001f4f52ada7731781b69ed93b7602441344dedcc359c385a897532f3475764e0ed3c80959cd95d47f958d237f39694cdad607b966b0d2b690844646b614fa9fd4f169fb4de7c2ff3b4805402e4408c5ba5be95cff6bb68c79104a58c11aac0f999ec307a230eba3869f9078c453af1539a9cf5f07dd18a10c2f2104a2007d52d55b8ba7e24d97002c461c2f21796055179f403a012554b2eb503bd7f62aa3542df13ce9b520c81f364b7bea0deda55e430ff52e0f7408e43e4f7289d93b2431ef21d80e04ca57538dc17b6a7b2f1729cd52ccf7f8b4d58fb2fbaef5c9b3cb38b34b179742e686b9fcb884286cbb9b35d70083d8dc6e6e9f374d473a79ffddf8547bcfc05d5da6250ff982c8692b51e6b219406d9eb1b39dd2adc8de3dd86510da7c383041ed1e87e5104d33478bb8bd7891e16c72eb23029dfb8b90c98c15377d0928a36a65b01b4e83e5c93e5a804a37513447ca0e64a945c0807ffb5f2ff06f270e9080eb0fe471b4241a9f66235b8066d425ef09e6d46e19717747d80f5a16964690b949b335b4c20d3d15a7eabd7e815856f3463565654eaa243f7a4c619289212014d1e1effd99390d1feb0d9a9a0b101a298aefbfa703582c5c2f6470c673a180cd6bd9adab2e6d044ba3f130e47b7340ba9a358b0600d1a8862a9c330e6ef1782ef1c912a4a60db9522cd3f1b941832abdce920a6bebc950d29e057d6a71e75f9056d601079109bbbb3b7bbc46b413934b902ea7bd0ed115553de7c51ce60ca100272221a880d3d2bb0b49f5b36a246df30b7fcfa3e924b5e0db2c55038289b1e266f91d504c14cc3722881721f46d864c9f5ca7c736ade9d7196c9fc59a4740d59d0bc1cc3845ab33c8ee7e539f2bcd8d7295e88803b1a66552fbe133a585962edc24ef8b8b3ee88d005c5817c9e65249c554a678b71fa1e6dabdea92b1ffcd004e16f289ff1758a491a2ebed7c070423b4dbbd61141d3357ee08bb9be71663d0d2ea59f4322ee39348fe8af117fb424ac49e362ccdeec78e99f7f2eef955f7454ce38c8dd963bc6a43bcf476514a5951637c64a71dcb65351b05c557d7485494cbbd0c206acc04002ca1545d1ad3f70cff600aeffa021fcc63d46b5128d466c87475a6b420af9e4648b3218449886a5f3448d2993609e7055566430b2e3f3d09a22633e19a1dd0418be9f99cee8be8336f575d3a55dc090deabc6786fd0d210b7e9577d69ef79beafbb541f96c6a01c9344653d7f84cef645989c9928b1738b4fb901dc9b9c29f4e0940fadc8403262e953ed8f6d772f1040b6ed52df64f7bddcab26bd6f5575e325fc81ede2c931f46c8cac6beb889961d6b2395d711261f9ddfd6be95e17f1aafa506e7d14bddb132b0821e7b06cc8597c8133878ca983bedabf66d0d2abadf4dfa38015d3e0afe9323c792dfccda5544c17624162ef0554b5cc6d77362208018717a53f44c2bffec644467d6b2bdddfd1ac55973d05edebd6e24c41223b8beceef1ea63410eed001c947429b115b864d785de238fa0e68411e9c5de226bfee699ab981ea86a67935faeb6f571f04d6dd1b73920a4afacea51c89a5561532cdb94a08da8e0bce1da552da943efdaf4a9549984337f0f5c4b42fd81a4f545e00d86f41ac6d54eb554efeba20896f3e1234f41c285636524b666b69a21bf19318a618c95a05bd4754318f696e0671662e9b37cd2393fb709e687a8ff171155d71e6ba436857d522aad38d34382c5b9f2c8e5b94464156cfbbefb3e390a935dfce72b9dbd2197e241b68cd9ef87de8165eb0515ecf534d5bdcd2b6a1540c092e26d99985085a95e31cff50452e89a3de519a0bc9494c66e77434242ef17bb0f4719b65b10cdead9b2e9072c59d334cb43827482fb92876e6fdb66dded5245e0f4a8aa6ac7bea417f3feddba61ee20b092fda1502551fe6cd0f14023fefe4d08767a0eaae297faf7352badf5ff9e58cf4d1fecb857ac3b9e83c90d19345474f82c074da1600172969606801ec567df6dbaa2df8a4380d3d500570b8461b2cc8511e5d3a45495b3bbe014440846892b11c3248decfa722419cd30aa94d9209872872820ad1b02b8abbce3b26e31aae5bcc816795d0bb3810c1e4f6ad22ff0a00ad709f93c3f63a9dafc2eb7cc6c8d986808e165b25dbed2af7ff5620ab09b3aae0928ea163fab6315b7885448f5579c40c35ed50340bef058ab56186940cb5fadc5b1d093c1a82ddd172a4d5cf3efd5c5297db8dba62e1a302127b7faac192a1de5f8225c49607768a7561dc138b0c4d9888fe68a5e38c125dcf23c7d9066c34c7536226cf68e8106c9eecf4925e48c6fb8286c5e4d48c136550d179a30daf27017aaa890abdcab582f67f4b7117257ffefc0bb8a8b6bab8c68894955bc13c185b64b5e790483145915881643222c50fc024002a7c619ac5561bb72704c2d46745ca5ee77311a3c22750ee8cadcc77dd037db697b939ce0e8be03f83c82948d491b38289483c8da893ebd9384f83f1ce0148a43065ea4e041c22aaac15a1a82dc6688c3f73f86f97c97ca7e259cf588f8ddf2de829970a0cc527df4d9514032a4161e3175c34dc46065c3a1f4c70a07734fa9bf76a3744d2764df6fb49dd8a97d85c62890d6cc094862a34ca78558c47c263fdeb1dd5dfa9b6c0ae2b3cc43b5fb742049ab38e87f9d50ef256fe3187506f8b462ac07b85f2395089291336d8233db5631cb1afc311f6b1bda3a2b8cbeecdf46085c5f9c0727f40c64681ff0adadfbc742a8a70d717a00080277f8fb67074c98ab4cdd2dba5b9a6d3defa9b6024bb7c452f19b74dc8363cc33b62ff9d5653f7f069723f1d6d7f7fdc38980bcd01d1867c2060dbc75a615933c2830e5a39885185d74f980c733afd19a830b12dadd006530b1514b5ef61de8b5b27ca58b9471029edf360e8bc4cc962d6ab28a1dc2083c5b98510f4c22f4577b9c416b4072f36b298ee1ce8dd8111c254340123e19509c179e4b71cb268381af7290196666e055a7de2715e6cc173bfa5af642a3fa942c85b07fac2f9970bacfa03e09d36da2f80b4f1a379e8e5b43d9e2890d551e6f37df150fe561212a3b0b714d13b46765dcbbac3f6ea01c976cd366da0de6bf601eb17230e6f3032f9a476ceb75d7d25ceab9f65bf93102daed84af1c7e336daf571bae47f98f74214d93403f55adb50693c9cdc1a2144f1544bcd1e441a34a8d2a2f48387130e569a9f3053d510e2bf379df90ed2064ebd503b44a2a729a19ae1af257508dee6133bc8cb0af76690be0b41248f3f447adb4679cbc91e93532513a9cac6dd4a587ffef01c202f6e46c3129567c833b5db32fb36054cef0ed13530c48cd3bb887137f41902fde6e4e472bd6e38f6a0992d363a0470a1f8f98ee17d528a0b4b187d6b0a931f2a0b123d50827d6b50797da8029a868f97e144f7586afb6b394dec7ca89d85be8a0ecec25863eb9215704eaafab2e277c3256dffadc6a99fb3bc94b4483a3fce40f29ecbe39c223a9d99adab90973c65ec64b9b9ba9f1d68ca967d87df7793323d898fd09065da2c6051308cd4e535634e235e6284fdb3697bd285eae40d57b976d7ac690e6a96b193a9c3ab3dfd771efce5cfaf12d48abd69023d7773ae998e7433600924435bc89163b7a18f7bada60f52ced09e50dd8401198fd48ad6741f545fa1b53ddd0b55e71baa55112e01f42731148ad13f696b63e307f3f9878c74bf513fb03b3bbcedd2234f8d299286add8e0f7aa4c7511b9cd080e293b0067b4bed86869a814fe4b2a5b5f2a9eb543795c7139211a61de2d5dad8c61969a0a460552589867d544b3561b8234e7d16ef4fe6590d141e3b29a77e967caaa25bfd1eed4537e5d57372e621f4f99825f99ff7016193038ad5f975fa64497d20f2d05ba4fd2d015ce6ae8d65d536584157d0a4269f5421c308818415235d15c2063bdd645329e270c4c6e180575ba5b20e98812520a41d415df1b7acbaa29d6600b797160d9e4b7f96cffbadc623456aaf9aeaad3fbdfebaed9b7725f4c5027d7acbdf36c3aa77ba6d2f834b5b45b2e7f1538cfc109a4817c913846a4d5a479d5f29e9b377144bb76265d1747644dda7465b498b3ebc4d361ac8817071b60ee23d0803d19a9da55619b430997ebc41c59d8feda62b9e1f287634b1b8b22f415c89444c01af938c50e0fc828996a044d5dc514479fde095d6e91c2e75e74af4dadd159e4e84d9dfb8e6e4e3fffd7f9a1dfebeb14816857dd1b6e197fe33d03f1835f91d2764d3cf8eea43ed03e9d4486cf3dc90f34a4f4e7f1e6b62dec4936284054322904ec54c8e3d618b4c8fea8c5985624b3625b01a904512da4cf3a5bb5d23d99fc6f72f636e08c23d435de96b0d52f16b68932e2a03b0ae8de987e2b9a324c3c1028236ffbbc85399dd8e441ce740ab549ed1547591469251aebad65b00fd4d60abf9e3072213e3ab5ecfa9ea0c1dd98141b6f58d244a6979af521e1d1c04b2de3db33a15b7e4845046a4f749b9a08fac86c32b958079a48a13743777aa000581a58151b3bd18f5b397a786d3323bce206a91013fa451a2e8c0c56cd428497e959b728e88a9707a80230850f82388c5f653adcedfee27ef1d045703dfa12952f9ab8859c610f3a9eddcfa6f3c470b7ec1cad5c06f69a65fa5e8641eff14a5378fdb63099cffdecb4816f5fe8fe64f715b432301523d2280be3e3bb2ff3374c5fe07f1342a37fef2b46a2726d308f9e678246448528a219ddde24baf73cbf4459791f564d9fc06b22635a0580b3ff21c9d5f6ec4433fac3eaddb055714aaceb48ac87af41f00fe3482a9888afa7a0811cbecbe64b264197ae22c1e17baae62116e79948771731bce85068dfa4016f3978e39ddb6373b398939642824f763dbbfdc002af17846352ea59cffbb3b07fd526936f313a9f36c818b9c8935d3535e05a7f8ad66541a202b3770e7b5b9ee000fc03f87a67ec1668c270bb6dbb46e1f58e999887cdaf5bfd08a899afbd1b1712a474fe6bdc0e3c6540072ede5b4fd2eb4da9b93bbda506a634eae7f9b0855c40609011911e765c5bf92c7ef9876d3898d1b9f74ae44dd10de3cc31b5056f219ccb522f6c48d8e32893118f6fdef1441251f5f1ec351b2ee7b95a2ae8d08edc4ce2d5749b2f07321ea546805fc4d46c27392fb66ec9ee04c41c8f77d4920dcbe5937cb5507795210b0acb300b3a9625fc4edb076cfd36c5f8f0dd519025af2eaccbc13747e1c7a848a5deafa9caddd682356e21d7d24d7b3458f8c2351c20a864a152fffa4d049b331de77b2de9490bbede98488158108571cb80c5e3eff895ba2a31a1d70aa1bc3b38cdf5610495f432a9f9369b9ceacbf45da63cf533e42df66df7c7e108146bd107fd890e32252e5e1125db9e5e938fc86a3ad8547951fcaacb69789e99bff56fb9e0350ec742665cae725faba2dd08de44a16d5779fd5c0a3624db0bd265d933494020efe58b64b6da26b0a78377fe8d55a2576e245791efbd9c2811c66374f1d95121ba4ee1b85b637fcbad348cc8b089936520a1c19391ec9186429689919c0692b665e95450bb37c8e88306f1d367cdde245b2174277da0cd799788d6ee9ae8453adcfccc3a5dc9832cdc59946ffbce28be4a4c0630691f5e9bb4f9277df568182fe478a4eac8b6576909c6780a7bcb0ce8f7d60f89e2d76d1fbff45d6390c1ac743129754aa2b7320101e0e7da7327d6c5e545feb5a0c48c6cacaf245503f87d7b7ad7ddb24522df40f94cea9a9044ee7de7216d533e16823ee8599181975253e5841c4a88f71b04d9465eee909c6b1e220d11da5bcfd01ab009ddda154828467adfa4c49cde3991a2062e10e74da1a6535ced9fed0b6d9f4c4f507d711d8879b883c43e7c3588dfc02a3218925feabe7dfc619d110bd5455cb79ad9cea0cbb5d2220c5d83c9a2169af546b260c4eb936a74db8d1538470c73fba41010963314b1ab662230d2bf126102eca5a8e02cf3fc9c3e406110d44c483b5a1770c0c97657b251ebfc59d2db5eac1cb2715aa3c5b65c1f4eae95b700bc4815b663b61d0cc7b5bc2b8270d7cd558edfbe3086cac8eff197ed25b4ca98546735f433db5ccf9d0b01f56400f9eae1656bfdaf81137c306211011e09b15dfbae35c8df774a3bba8e40cd3d4fb00a6880e445324f26d61a612acbdd0737cae1179bf26bc29b7cb0b23a907ea011402947c0cebd8aaa82620a36ab9b6d281470d6f05a42c99b87981f7c45adcd01b2b47026eac614e0930976ba5b0ca56e81fc5277d80a9c7ab49d670ed03dd760af98a47e495a3fe92c0f97c4138958044faf55c5f4ab7833f1db1245522924609028ce1c234bae58d99e377eb82856c7a8fbfc6a21f3f2c21001badd0aab473cfdecd9a89d230ff477d43739debadf3c7d03de3987d4715ac54ccfbc271ef5796428faee43cb46feea3a73ca2acd73685d6be66e86a958a8f29ddf8a7d3f9f444b399dfab5a6e46aff3d3ae8d25a4c737e4c2bd1660b0302d2381ee4dc9980fe75e39da89af19310d61a4042f56d22fa2eab5499eb2865197ef07226c05600fd904f67feabb07a7b6f5fcd31302755a19c28f3fd8fb34849afc27a79c226e91645642229a0415ecb6279770561ea342e043baf8907a6e6a31914bfd8295ba6926bf4cfa11a369e10895d5d4ced4c426b69f6084a304804083020e812d01b0b6e6591dea9145ae40fe4fc0e820c2a3dc813bd6b1e960adb54628f1fc5b66df7e33cc9cfefcefaf14a510ecc1ee5a7bd5656a34b14d3bbd6e20034b421233e24d7d8d80b51aab12a1d55408e3679bb3c68997729b2488594ba5d8ac0deab6b93fc38ee6db89eaa263d34822bc71ca45234db23ec5a6826540781358a9f9f360b863204e81d07046a093dcb0c9bc3c0415b7c45722eee2ac88627823b78421d11f0be2d32590c2380f32d355229e34e33e44ea3ad47be9021aa521b3832b8edde4a37368ee10cc09141822d5dba7c5b50705b4af3e94e3f9ca32840146fd4f5f6910de5004fd23c3089556a0fa92e2645b9a3b9f4ac9e850a3d3e7b5b8d32fc8bd9ebd30d9d5230b04c9f21073dec23366748560c6fc67d741a822c54ebe373e676e515959056a805f09852a111a6beedec8c1ed6d8133e24d04fafeab7b877b71bd107db03e7a47219111a98d8967405396fa2a12e30e5c02b0235d6ab4c414d14393e6bafe123ba93f1c24fa303313ecd123af3c816a898e8c015c977550012256d83c8f96da6129de1b2bd857afee09a2122c264f5535ee17207e07250e756051b665c5130945b98cba09141f4175a1dbfc6fc3440f6a2692f0141a980b856131e75a7f0072305c0065b5b0b32d0a8a406f136a67d8bb51974a46cc1fcaebeede69c8f76e9cddc5c040ef380555ade3492dde6f9caa0d4c18d02106389c3334dd85e21e5d1198bd95e32a97fcb59c85de999cb078d9d2982408cae15ae6fe9fa95b4b5443e8d6effcf4b3e903f021f42644d1605bd52861a520f413bcf9c5fba40195bd534ced9088357fc3155c7606ec5b857eed52106d4472dfd4220c46c88a2e444b0f0ffee688cd472ff90304853bacbe594e7e9eec897b6f6fb0757a658f76ac8a36f995f228db232ff365c842a95ce4dedeccc7d6adcc66f9723b413de9f410bb3a62b338b3bae5e9115fa66d581a5d459ec0bcb09a013e63303e89073cb8473896915f1593c9a62c060a64c8d164cae46388ce6751a7d11e1246af906e1fac401016bc7b8ba49be563af3fe509c1efc2771bb2fab8827c60cb79de9a1ad7485cbf6f145c4117a93b2f476300c403a1fbc48af19666556697857a6422675ee39868e4d694b538a5d90a741829ea1eb7f16a4cb74dd92eb71cfa84b5ed5eef1f5a52232b53d8791b48fb98cb0bf41a881f11d0d202a40d5031ac03961c345750b8d0846f28f76e663b931c0aed8b5dab097d144826ebe16b8e4362868b4b105ae28e4c458982b0861c0c5ee362ca4a714dff9ce082629e21c612546e4cc50fc78dec5ff9bb6cfb7477499e90b3d799fcd26bc460448ae038f4d7dfae99e1ff63d34285a15592955e50691548ec56edf4493f54e90abe856c933b365953c683767622c31bd2b18c50eab5e1e8ae29cba3dbad2199a4fedeac65f4fcb0094524ef23a1d0001147cf902c092e82eb5e2b80340ca64adda9d479af18673875eb661b3f085a016b7fd21bee7789755ddb38555338d36bf6277947fc8debe80d3ca65934882e6b03ae5426dcbfb40efe0de27d5dd33ffe91d8151327b8d49232495a021c7d52b1a70810d270f473b9c0a9481744b0b51ba5a3b7f1f8f86c2f23da41f61c2bb60b545bafb0f7294aa9867e2d1c2c5a1b4bcad31373e8fcd29118f455d0334c9db169a438fc9ef021aac8d8efcedd2fa51750706d4a4fb95d7bb5d91e86d6b8b10b87bd82aaa18c2074e54f9ebfd46c1659a988015c86b7ce187b83fa89e27ba3e91b6e1c1bad902723f0f1a05a9d016e0e6b587ac9b3f1c9985d1ab1975a5427bf34b812e87130b44812cd2e4c5dffc0752c13c523bfc86baaf12a256141f12f33a727abe44c948d58aa76f91df64f063c36b9a6d04dcdf7201dd7c3ffa1d45eaee5ed72b85dc589bf8147ef5fef4ae4b949c2f9add64cc0b45b05723ee3701165b4a5eb8a442dc0af576512b0272338702afaaf13c3fdcaf4854a010733c4c56d1abfabb061cc40b0dab4b6c5861fffe5e3b8294d393a368c38196036b257f70d227b32140708516a1b97c9c50ddba5ab246eaacb193129cff1a51ba69b4744b712ad3477c6fe36a940b6aa5ced1615fd0f9e25ae89812eb682b8a7a376c1e8d08e589cd9e3d29b25ac51e3e8e3ce7d4b3f8b7fe1ac105c7ee26e3f467cddfd77a5da729943a14ce537cf7f54170293996834a4206521a03da2e14bf28ff11ec8288912d13a08631e01278eb5fd631c7ed1f2a2c72f22f7d7b96cb27c27da9ee304f70753287d4e288e53b2781b8dc405379154da26904290850422ff4b396a1352ebe08c7136efe738fe6a38d39b4275127b9f230f437cea7df223b3b31bbd88aeaae1f7ab5e93d39b7f4efc5f2bcb6fafa8ab485f9c121cf9526fca23f3213c5ac138190c9458236557618eb79c0227678f9fa5557a9c214530bf6b858cdb46cb259092425ce63de420298a7ec7ee62b8b5eb61a3df4a479b68e0ccf7ae1cf4908c0b5ff2f34383fbf155c94288614ae02b8f1c4d30fe332ac7a87d45bf55c9fe2fd593d6160b92795ce24cebac2ee6c2803087515b8d44c9a5953bcef88124af2e82f01e66a2ed8e3d68260ab9dfa887036954d3bb9d9b43860bd482a5c8ef96aac05909de00e73d96063256dc875c08327725ae2c71259001b49acb348cc3e2db38372a67cb2fbcb136be5597a6207e801b08430bb5c33e9d1880621a7508960cd3df0c36ccfc9d0bf417fb43239a72490b1d536beb736ee74d37e4745d15b1a860c7c43532c51621414ea153daf68fb09f0eeb8915f0e973013dffdba60c8d7bc807651ec4227c0aa4fab2b04001fa8d27b3743a75e91579551d1c45d1b47994783e5b9b79eba1245f2c779e856343fb34b15b9ef2bc2fc7db25df53f0880184b6afbd88813ca38930d58c6bc371e1e0c6729baff00def2818eaa51ba356d6baddab556c59f198695d58621ca609d187d9937bd4dbaf0a55dfe855d2c6f42df4906eec6565c283684b51f36d642cfc88841292b899f4ffbe6c7bbed34db11ba1646de299d752cfaf3ffb98c163ba8affa22b4b96221991b48a9e51dd5135042e2e90298088da604b8cf7756f6c9904a55ab157f508a028de1cfb65e81ea8065f20b7ed44c15a8687351bdf200aceeadd5542e9e33dd85079a46b54ee44712d8cdf2b7a672d1607171a9c00221cb04c256f0a355d7c98cecc9035ad45032fa6a053333e419250f4b57a026d72b341fd90c4ee43808c70569058a7f391962f39158d84d0c684fa4213551d76fca21d56836a64eb638b1a1b8b9f9d22b2bcaf3bafd6d4ac633dff5556f622ee96f4512aade6f336de2ceafa3efe78d20a4e5a66549a503a44fcc91704b5e865f451fafd8ecddda1d09315f2f2938df845c1f4bbc55615f1d3fff74cd263b6a60b5ca1ed8fdc3e1d69f792ed688d95cd679873ce958458990f2aff5dc86292744cb698d93a37da64965572f663cd4b2203a80685bfb55936bba99b9a6c2e0d6cad1e84b6b62632110dce3b4995f481cdb308b9b9c13f2cee0c92f552b148eb2493c63ff3c8520c4b6660522f7d314f6e5615b81e97c4febd1ddd66df9617814b0f84324d683dbfd082d803ae2e6fbd76ef66fd9a24f1e63c3f3335a2f61554f8be4dd0cc888afa85645372da0ee6d6730d32e5b5c7637564e7590d73f3a86d5a7cfc3fb0079faec67f8f6891d70936993d0b3dd6a385c98b45830b3ec76864347b3fbacc90779cc5fde43af74b5c327502fc6e2f0c4e0533d4d30aeca71273a54aeb815699d38781fdb6871992591104a2617fe6dfb5c628d95d1f6caa29194305f5fe15cfc76d1d18509fba3a09c208aada3edb8fbfadcec634f30866727260bdbd3bfd751700bf9f78a3e896ad23db343877e7ca7b8f0af80cd97ce764ef93c8f5eeae86cbfc196c4137bd0d5134888ec2247e62297928ffe4105c1e40d865264d1d046ae2e785dc369daa550c4861ef870913916694bd0baf4257662b413356ea72cfba8872eb522c6e1f7db5604916d6ecf9d4c8f74999f45af1b486a886c8cf6cc7b8cb8de8b6f9a7e1a6d445a1a0cd69cc3047f4a49a78cb9f0af584d2508c1708c912de3e9567c2d93a5dfa595199aeea8e9600e9193bec7f61b7bc5032b59c653f654b1a2385b5f11266ae2b7cc9ee7154a2ec5b82be245f37aa82b7af41d11e827f965f7819937d2cfafb34e2d67f8afcd63a49c68c2882937378e7d5b1d89bb72f4eb341ba6feac61fab677d42feb8899f763db4c1dba15014c852edeae1ebb9218b36fc465a4f29d6bad08821f7838777559953dfb9b80a164de1f0b757f193be8a8572231c799bffa981a89afde411e7789a567a462d7d0fcd351695f09bbbc46fd96fe4b875135110c740062992867616e920a078148dee0d87a47161c84de9b4061480cf6c184bdfd8b2dfc0d668287d7206b52ea84abe8692c9bda5318fddcc59555cc856fa88795cf981f987fe6a2ec5382bc9bfd9e5c61a9bd29d6c30b72509911980ed85e5f1a2baf1d630b59ed80eae8b7bb8811c1fb43115bf0255a5608345e00a401b82938c5def0a553b921d4a33998dab73aef8dcc94a84629876be7ca1222bb03623c2a684f95acd7cf0829ecd1cb1323a3c2837476b9563f9d3deef4898bc50b48d062eb55f77fba9b929805a612b004e4d67ed7dc3db2de2ff23e696b525334e3645015e3a5926ba734e3a615c037d5795d180fe269ede800d6d505c92b08a35331e49d8e91fd41fadad67480489a1fc165ff593700bab7b98daaf6f47dae85c36e267e8517f7992517da7f35c4a741128b08484d34d9468fe66977bfd06500e286cf246d65a8121be3a6392ea1c011f110882c2c2b6ea8ac13858864fb1d8ec8d3b00238bdf7f17b0e1719b13642424337a35e188503dc4a5439876e201158a082c2035e2c39cac23abf0966a44c659bff7580e063fc565db8d0b319afc7e16062b3bf5f840cbaa07b589d0a68dfcfe75b78d67b2a76e97dcd72a5450dad5ee69b61e4e7717afe68e92d03f0d992a3568590d028d1b4382644b383756541dd7562216671b936ba8fcb01243f03ed4b540fd2a3ab2834583419899b72f5912b4a77e39b638db313eac03b17de82f4576f1578762b83948a7f86ec118a1cfb545d3bbeb33b9f54e2171b880fa9f4a312b721cea7d58b8626cf3ec13c1f1e9d0ad59682400d18beb96b512e5f6d5564926565afe7586162180f6dffbce79600eeb81cfc7465c5820d40993e3b1cc69cb79ee6342dde0c93b89a4b0b57e907ecbcef47f5332fc0fe265d7f4a813cde04f2e32f24b541f6d0f28ba53d632a9317bca045afb660ff41986b421ec5693a0b8c78279f636118e618f192330ecf34f533d3af8f6529bcf5919cb2d4222e45b546ad303f27f655f4d93a867fd929cba132a460b7063146633826809f051392d0c9362408c6e21182108e72538a04b6bf8c80745b3d1446e15b80c26cd25fca5a2f06e2afa0b73041d5e020d8114aed6dcfa8f3692aa08fae2cdc0fa49630bc4520b1c70dc4ff22b9ab64d589a1c8b9d9b15a87c8e1514f7f328db92b0ff5b1b56a95c136b0302697309bc92a0dde9f69db9527ee1d5b42aa2ae05d31d5f84ae266219c8ad8de036bec43d3b65b326645430abf28f3a578167c2b2abe0f20eaad0cc78735bfc0159598070ec2ee2a448a52492bedc6f5743cdaac00a5e0b493a82f16ded5a7760b4ba7db565a96aaa2629caed538d23999286b0395a59b6a32419413b3e3ef5e52c504faae2f54f3801389ff2aba485af2c46cd1218d94814d46dc6733590be1e559da9e71f45a60faffa607d39b790a4c14545b18f6aa9f38f9149451273ddf464501dfd67a55c0f95b9bcb7d378d523c52d80b1066ef5e51baa34bc5575589586ff31c8beec4473cf78072ad7bbb3191abc15c701d3d8f390e91dd0d99d02dcc7129358895ef6b31544fa071e56b8f9bbd74e023b6358f4c7452bc6c22400029f53fff4819f986c287fc4f26c8f071565faa3e1da2b6438745512949996c88b54e39c0dc72f389f7aac0e69158e10906f9ad41db237c507b8e7b0f24f1815c23ed0a9b2da7618196b0b6f50bf8a9b941112b2264709ed2797791d63ac0ea0257aa37a8e3353a2dfb0e90000ea384459a465b6cff2f0671046bac98042c3b9ea9f4432808c6354ab0adf0063448e7d9cc9bdc53963d183411dbfb7209b4ece4b6250a5861364347b4f4bad8de949d5413dd4651071db6eb77c6fa730d312754f3541ccd81c29f24464cc4d370fc6504e6710902538eb75fbad327d6e74c90b2b346dfeb9234522f5e4735259222088b92e1402b4b9e9a2d5ddbd6aab5e47c8abd3f7905d52a832d701e44f8c03b85410206cb5432fdc6b925182f119663f312ec175806c6ade1fa07ae9a43cfa2f7495775ca947d66c3faa0a087620f78cbeaff99955f762e8d82a2ec20c2a5282a87f716c96a5d6a5de2043713e197399bfcaafdaa75d5579ec6514708e60cd6c7a52b9ff57efbe70ab973a5a5ddb7e23cbf19906de5ece03fd1b7c58436ae574bb51b467ac65a0b5684c4d625a3776b10590fac1a9ce8fbabac4bbc369f58492a4734bc57644fb4ff3bdf090fd4db40afb5fd39d8419b3db20b090e766ef4d24fd28a698e12bd94c1961a6d0d26404865e920b5a08dcbb9dda04391b4bb9c4262522139195ace73343bcd2ab056167ed0ff0b98299dec424970d87d325a2bf52cd3ad2641e3459996dcfa0003dd17de89f1c457dac80e3beed0ce574491848762e1163e6d3fc7e876f0209fe6cd101e45b99f7943cab69f6186d9bf6d8e520d7539763f5aa2fb4e43491e6c2142608dbed4c91fc1db473b3576b8e3c1fff7432112e99988ca7a6831a9242eb3b411f03d215e1748b4d15d8188a7e631acc38d59c3d144c1335816a22e0cb5ba99d7be75310a893bb59dff8fc1689c64088723aebe4da7da792ac20e8ff5d4dad89a5ad69f29f596c73bc3a4313eb3bc25312e255f4c895ace26cca38bdf7afa9698ad4a410dadfa286aeb8d5f10c467b6df332127c682b989308b9ac6d77866cf9c9cab9c7148338a4ce3b9e39ebc4d7e1dd5abcd5ecdbb25dcd41a74eb40afa638f6ee1ebbaa31598d5e533147dbed0f4121b9759f32444935800ff21b23f1a4f53daf22d45e680637bc82eaff186540ffbd82831965377fc5374ecc057fab5b606d45df49d1e46b7ea15c42b77740348d3deba6eb02784f09443e251be7beec1728db61c8e6b7a46d9f61c25854c8cc8262afd14a20a37de4cd8bfec05d5a3d603b3f0665c5fa21be03054cd70773fe92364fbc2776b591a279fe000bc52ff0de9b5995ffefa0ab11324ecb4d7848761bf855fd8149a896c9ba8263cc27a90ebcd2f9480cba5ff5d4726cacbbb55562692c74645a2df6e0c965bcc907a7a243e5e6699af637fb252ad071c47e2d5f5538c4d4301c9c176bff0abea559b6c42bf1d0ce04873b99350605bad00e0577967414820a8e5f5b2316d868c9dd5cfd40b2ef35ca7bfec53aeb32a94dd9341b8d629200518776874aae6c8e3281196458645fd97f223da99034b4b3324bcd1a4c51538d363eb2f3b46713831f58fc5411939b6bfde6e0cdc72417090fb4e0905831ba58fd8ea4ea0b9039757e5c83bf635977770bdc802554b54ecdca275dbd921aeeefc84c934f19960f7125a926ef85e30f87b045c2faa23639860a5611d3b4b88deb464d8ae37d53c0125892e226024fb7b1282819f282531dd36ba9f1d6abdba037acf1f43a3fc65f793a858991289a2d00417f8b09fec15c7c0a3d863e583069b93a5b0789150ecaa8be5d54cb88625487eda5dc5996a95c2e15a46fe4ac3ffaee66c0cdb16bbc7ef90894bc427aedc5beb2144c02f6fa9ed8d42a51229eabab54ba7792dadbc0f11b73c18fe5a15610f4530e54610104d7b8ccc14108b8fc4a19a0573250200b070f41a8e5fea8826f0173d8965adcbe90d3810e2e35e9ab038f2631bbcc18af94177a0f1c0af02566971af4ffd6f5bff94a3d5548f9fd9ed9bdee151a4a3f6202a9271fcb4635dd915e0a512df7ca7d60047cb6dc5c128df391875ef3b09701ce7165c9798f2604fd25eb078f2992ce671cadd4313054d80462090290e1a62164dc5ac96f45c0f12654c797f6269a37b12ec4e3b1e5e4d9e5dd17f5262ab1a7ebb873f035aa268ef9e374d6521aae6d7af2d6d65d836e79d6461c5aafe2c91ec35374a038b9ec89748c1693122ac6028d391d6f5143b74b842f53493d5bf6956d26a33c63c9b10994034a36001ddde26e5b8b6c1b8ee76bb94748b1fbe8d2e08fcfc31e2fc459f518282a609c1f97296185ae280c4318137bc411b8d097c40af0a11c2685e1ea3b37f425bb4c78766a235d219531d2cacfc12404ce3de0ddd4712f84671acae9bb139816bbcffedc1a6c273f4d86457af7aff6132e94cb4165e90b16491df1e67ffc0fca1c227a10be9c46447cfd0005a0153fb3b535858ee2cce73ec76ee4aecb5b73cfe4565a1a0ed1f5d582b1e7d244a413e93f5ada361a58443fbe0edc99c1ee3368c7583b70c21bab896491c86aea126b6779294ad1b36fa07ea2b780d4a19c5b0ecef284d444537bbe741c4cfd6987ce20f6c6765ee90d38fc7f2d587d3a40cfd79f312ac44ac734adcd418741d2bb793a468ae43c854ba982e9f5667a488e40b4aeba0eaf81e17b0fd0c76a3326591ce94be7290f30bb23307dc78d6b69975455d6cd925b09bfcbb43a591f9b509508138f6f04aa7680111ea6a280195cf3af9ca06958cc7954db0c53545f473207520acf19153c1219c25b14bd52d9e8ef2efe8fd44716de55f4f3316970ce80076bbc4ca37b93ae358389c5b72f0eaaabd6b500ce63e26fea094dc76e4e7efc0adac93388f5b501f572b9b482d6586a9f9a678fd5f90491eb94dc7359bcb27308b74bfd4541e13c76c29ed6b425beb4a2b68ceeb49503fc4226e980d0fdc1dcaa97f962b2dae3fa4628e1161be82d56b87234ed079daf0cdb95b8eff084f5dc693ab6bc906befb0323eac3974cea284647cf84c3398d1289a4c798daf996d8a1a77b0e326c8241fdc35aae4112f0d306691b533fea134585d93f86239d4aec0907c0476779b9f0255f7ba0c48463c0728904f3f49da65745ba82c6d43a1f69fe0a3939a9dbd0a1a326c8f122467f7a474a1f34db3eae662385e671c45d9d764ee7480f9951044ba58d7202bc749b80f7560df0636cb567dde48d4a40e05b06a02492da43408c7eee67e830692bd56dfa83f2a83925fab3341fd88abc0bc5c912ffd3e989a4809410bc7a88a586a81bf6b7790acd86b4afb10c88cbadcde886424d5bea51773cdd5e3e5cd4b48e61f50fff2d9633fb0ab1dd76df3de572066b9659b18c94c682993a1b63577a8591032e346201c08c821c1515ca4521bdbaf1be924092eadeeb685fec9b2e94e5e7a8129068000c508d07d1059ecb2c44cbc0b5c35e209d04ebde92d82388a6a40bfa63e6d9e185c09857aa0d56047e118771a7801f72d31d91b2a9213d9211a49b49273ce519ac51236030d485f42f08ac720bc1c29c97648784d183cd4cba06b51f55eff0bed608b9a9c05f21e9c6243ef9f294fb23b41028bb9e41fc3e9564916659b5f890ef7a3e49f22981e008a83210566c9a1b6d29a341a12d947601bbbe20e8a2ff2f4bfbb0b7856b4a330eeea7e24643a853884ffeaa0c104d27a4b31f9082506eccfa428d821731e2e62f7af51165e0665b07e26048e6fcbe046bded2bf5d295b29d3b59f119d4b16a8aaa926fa044a9c4caf678041708cf383e5c494f9285471a32e0ec1eb9d363305d9b6645f719d6209d1a7cbb8facb0e40f305ba618c4d36a07289a0e44c957ae861fca29399d576c3a35c1e587381078a7b1ef99155dfd7f981d352b88ccb039d344f5d04f0cec3e14906aa1fed6c5a05853ea70c1dd06aa50266b8f687478e1e2e68a029024fa4a8072a586e336f9b47b947d7594c284a42daa1b2da26091c61f80cb82f7f22b3aaeff341999d1b8af2dc496bb674de530041925e34b039a70a5bd4df33e99cafed534296c8bfc593cd48eaa757e57fb8483e42d98335571272fd84eb0bdd4dc77e4e78d631b7b61c769809bbaba76bc6d5f38884569b2fa542b71054a3f4e5ae1808e8022c1f02024c6144a890b9c37aca0905745d2cd0ac292cf0f91dc20e81dea89a8be98bdde3e2898c02e52fcf8d6193b9a3c7ce2b311a4e764e3980a902f016a1a4fb41267ddc2966f82b8324f967f0b2a3cdca7835491360c23deeae74171489e78ca496d04235576266b834e5971b9fcf2c2cba6bd4803caa4675d6efe7811a90233012d1078c5f7f037e403770ab5d7e4f3171f299547cc45ad7a02c453314f5f2cc40519895f0b4d9bd6bfb70d6a69609979435c41d7dc816d9a8ef9b1ae1ac7f27a281cc93ab680cb6ccb9bef638381308afb37be762f2da5229025f2746abc1dd54732bd8b831994c669ec07b3110ec7cbc00753ed4e40fd0c7284842092aa1636963cad046c97ebc5dd3d732dc94a30b3c3ebcfe9bdf40e550cbd1d229b1907d49832d5d321903db8037743da1ac98e6357f8b65d1add1c2c37573c5ee8843fdac1a938565609f3bd5a76c92bb8dd1418fa12f74048bf1495007a60b1f015a469bbc47bfabd1fe65e6c67459a6a1aa218b747b52c3bf8b51b582332f2004875c7a0357a87b0a04ad01678b107f9a2b9f3039801e7d204462df86096aef837f6d71f9ab6a08d17e91c05de652507879b27e7dc6870c2618b4227aa50f2c3efb8c1568610e179bf578c13a162e54a59cff0e08157198b56957a89e421e9dfba696b3f42ed0b9ebf0cc70608463cc4c376120087bfae5900a5d3c9ddb097aad563b5af2f6abd112323b349d89702c313b0e4002df7a3664c188821c8c7f0571007570c6215c9bd603c53435a77da0b228642d30eb6e84eeb3d0e4564764544edafa987a78def852d72bf838b244a29c1065bf24afdf69f683ea11c7127efd9635a1aa05cc6970f9d312287950a6c4d2117cc2ae844642ffe1d2b004cdf93f2da62081f3957be5995dcd97b590a997b9a093f64db2f861659dbc80c8587044db8247ed16db8eda9bc32fe068d20790725412c66ab95ea879a373a607e9cdb5a62a693c01f7a0a4031ad8de040d28ca5448cc9982fe7304cdf6c6deb9b4782b3cdff0f8cdc00ef05bf3c6ed3258eb90b01e6ab32652c35f8133223755d7ebd73c24989e225aaa168afcde29289b4771596e885005b3d6d65c73a5509202b68f96fedfec2121326488ef60b101e8e60d149d69ede2b08782be1c7aaa9e0c5255dfcab36e5459a25d8f447f868ebfc9ce939471fc807b77eb5a00c653331872c774fb68c212c343a8bd3fc9bc52f2ecbbde2fb2ea9605c0fe6a7dc02b5cdd0f84974cade1832939d2dc0f383626e1bcc3d9193d96d8fec338617ae0c5ad75466d9050a4a7730dd61a87ca62a2143926d7ae8cef2333268d2274155e297d499f7730685c2fd9c863b97c7a3dd01fc18fc3bf42354fe6246d6b78987dcfbd06269081f8975c515d351b1cb8cbfa6c43925d51666a279ce13bc79b6136f195cddc998a18d1f0ad5c9997dbf8c0643280a775a829e7138b5051207e1b2e57f680f82cb3a3484bdf7626dd3267bde824c3ee7995f6f8cff35e71de6c612a06dbe344fc8745adee861dd7959c2b5b3aed5fbc22e65bba308932dc5ee90071ad7b920c5698683fbe7de1e980303df957881b54f1b750654ea3f00aad73efd82b6084dbc646f38b2ac19269d213a0444584e7bd49a30f011f5a2f6bc158c4ffb52aba4e34b212c01368e2ceb565df8cae2dd4b2e1a30cd06e019807104d08238b53ad30e231c4f8d1440e14c21c842ff709648e78b3ea80a29f726113a19918f1f74e39f5052d63d98e66005341688f57ec438b7c6cb90d21a2a86e695ffd48f242e6f642b55194a40fbafd0198f8914c6fe5930fd152d0286ae1bd23848e27fe3446836b2d76b960caa119421fe7f4e7df092e00a7019cd58301882e878600a3970c970c33118d43d13c33af57ddb66085dbf02a9ef4d09b5acd5c6e9eb48fa140713b7987ede4fff7ee835263d99db9df706abad165ff5c56debeb5791cd5b0bb1e0477d139c24d66944093d2a5743fd72b8960b4d602836c536a942ca45a2fbd29ab9d8bd7ae7d65e64f292f26fc96d7088909420db9b303ac963c427a4d05681c4006afb48aac617368da5661c5278a30cfa002268cb3195505b2ace8c8cae1f1fd5b34186798f56ecababaf0b790781e04d08eddd1da8af98950ced6dfee3caa49a78b16ca7049ae0e378da7c9ccd216a06f98cec591879d8ef1ce2ea2d1fd01f55eb92f606a70baf40c999583829a895d74645255202e2daaa498c77f193c06e563a9b3561db8983448cd6d0c9d06a148ceb58082e362864290f38139c148c3d802a3aff13a2e9b9cd5486d89f68478ad22eb6c68f199726e4788cfc2d3ef9d4ede892a99989b5d06b31bc9b76b9374e8ba0b46723ca16bc42097b1666cb762af7d04d78547d7cc036bc211a3ac9b1ea706a2946730b67b50a34fcd135bde01515004b70a7c5c7b588bddb0984b2ba516add041585d78328cf8bc39d081727dd794b7460002fe136f0f2c4005d70a0ea40f11a74857ea18b78dc79a04552d7929b1a4a279aa6ff8cfaf1dbed8c2417f4f86adb54707366df2185f97e5b58ef83bbfa513f0dc53da5cda1b83d8449e3c7868b9a194f962a99b567743ed416fc4eeb390b729ce72db98b7e37690a4ef0578aad7d3975d57e4c982f74e2f1c7902096d2fb31f58512acbfbd480dce54b1d7b5ea1b2493d610f03d3bff2153dbf2a2a4887eb6bd335192cfaa6c3f48a6a83044dc24ca5c3324197bb0ef6c126e4ef9129b6113758c075a0e9afca077e8c92940836b71c1ab34f35a4345b96f437d735be8aef40f202f281e6bc178c65b38aa65da2e8ca183d14aef4c86cc88cb6d506882cbde320ae5a77547f52ef997c3a40f173464e1060380e5a4900e29d081cc34191945c1a7dc69a40e55ede68636c6673752e3adf9e5a217384dd240af5d1cf28b37c7559b964d41ecbdb6e70b9f209fe818c9cbee404298e4863ce600dab3716abf797ad951fa831df1a85a3752e31ec7a1cc1115732e7d537990ba7ac238d14862dc7c256d21e1af90009479a93c28c86d3077931118be2d5bd79640187ac12722e0da3e0582e86efa6e2f5f7b463049311fa0af24b043167b4ac57cc6d0e4058d5e46d7533be851c397c646fa0cde2220235c305ca8729affb67ef3dc3c5a7a1e0dc58e2cc5ed92e4da36072cd55244ea5a2ca061ffb1ecc83b0953fc527c2321acc14df92f9f650c581152315728da425b6e117a3f089897482a5394d363a9943e1160bf113652a510381e9c194bf52fb1986a098e17d8b8594a923b2f7beaa095ecb50ae11d94591f4f4e2332dd85695e588d1fa66611c7cc47202605027fb2512f587355f78e193332ff6b98af8b48a30ed56b498c2676e06921ec6bd322d00064e5d2417fe5f7af871773a80ce8234b5a2b178be151bc79776191bab61a480874edd3740945fc8da656cbfd22d5bae2ca28cedc420876445dc02d3d5d987e7389a972ae7a8aad24c77e97bda20aee0ef68b1014afc1533422728108eb6c21417784ff97bf88493404aa3d485d4d738ccbf5f99188b4723b13739ca1b158341cc62a222e1e5e7fa225ad60dd8152ae44975642622aa4ed6c5615bd309d4e2c793dcfe5a089b58045307fef21892b76244db2e788693899e21348de1c7262ad54a6e9acc2308b71457b725507f175da4497be1b835d3f483eff576d8ec13aa3c57b7b11a0afd8a477b270813eea4600f6c919162e2aa2764f13335b917e13e85f71d4b5aa672389caba88cb547cc81e7cc8c31e88a64038534027af918723e9ae5e7ec32c5b68b8fe6a803b19775648faaeba5d36e7486b9c68041485e8598701748201b31e8245bddb180b17e2230954cef73d3815afabf991dfab9071fadbdfb2b153f75ce519f8f7b479e752790c64d18f32ec7ca29359c994a75206a5c23d5f341d784c1dd6bb6df3caa4ec39dee34e8b65dab6cf22e599bd08b72f686a91807284a7976ca613b5f3969ae4236027b0cc2f34018bb0485bf2222b5ee8cd9b74d60b9e4923e151e29f208268ca9b22514e8e2f8cdd1f1957d68056def464f63fb35836688bb3c444afea76c355639f3b43c0f14832b5313892bcd073da802b1c58433aff0d6d8712e375f6f9c6defc9a6d7d26e66d066809924ab0fc3ad227af43acd3f67fb9a2aab0c2a0a6399154bc988a3fe4983e841b9c79d89ad26c6a3abf912b43c16c58e23d84798856d5e802c4fadaabd41b7084d2d20a7e8a775b5b41f27f9fa7318129957b4e3b6261812c0db74f8ab21f9a810f2c2f979147c31d310b502893ea9bf3e62ad2bd6b088ae4cd3d268883918ff230a958cfb6009fde57defa7745d156795063b92a27aa598efd559db04d336414fcf059876b389ae2a9843e91f22b181dfb2c31f76fd3acdd15d3050ea7e91d4e2978899d0cb53689f687f2a2eef9191e55de8d397b21f9dcbabc170b5cf930b192780b07cbe27720dc2cb66773dd467b7715cb278beb1e3c7392fa3801248a1e848ae3136a371a172d6db3c5e4221492b011c336c47654d0923448d1905661944d58d83fcf968e6b7e09d28f957f6a7ab52b7554d938440a4107a272186f4679d31b1c1d8656f07cb452a2fec1abbd28a3efdb5bc25945563958d6f38faab7dbc0a54b44ba71db2169316f1642f111e69aa0d7126851f264a85c0451f799d8ab080b3528cdd9c1ebebd987e2d660f845313faf4e493b74d93b9839a556bfdbbc2346906e766a5465a8307f232ac0040a56dc969abc2e7519c86815a6d2481944d77175f8f8f6f666f82504f99b5a3368884d07cddc4c92e81ba68ec91ad05f3b9ed1aae4506e3974018c95665ad249f21c6882048854973c71090afb6f8222df5175952b9eaed98f90886b43d36bae4bfc9c8060786309a5cb1c5a47ac26e78f9cabdd84d870041ed102553e8331c06e81fc6646c851d60fbfc4f4df5be47347ee07d3257f923b02331ef9602c566bc2e008b6bc48d9586ce9cf9d3adde5e67cda07655822e00f66b95c32676e22d0ba262d04045a3e6cfa9e2336af3301cc5e23def47e595355271d08650389c4dc7cc25993a84c62b6a95cc700f0eb83231af1bafca03da774c0d8cd4531ee4e3e4a5ef11a5c4819eac5a00fe960da5e4f50c5a9bfc92ad7686ea7d652c3ea9643bfb829bc2932c255b87171d9d3198ed794cb2887291913ae84bc27b5142ca6a2de9eb3a302ff3d8f817d8d7dc2bc9f6bca071599446cfc78cdb58837119f200d44cd16160b4afed472139c34ab8d8f76df4070bf1c520019de28f486e5516fcbb9af1a1d015987fbceae835c0fc165e35b73b9d9608963c0cdd0808cf7ff680ca3bffd60ecdc63ad7e345263253c01df836540bba7319dafcef0f63431899f1c7f58ea4b37ea3d8d4e9a5698c5437a391220707e34af9017e37867bc1ec766c6a24108ef4dbf67130d23eec4f3299a127be649f5d48b478a83d8c81ab29e91e1157327a3f6d2f36957ee21a1f6207a586f3d5a463974967c13e5a219f6d3d3e688587eb8b4d8d914cbf341378d0f25c681026c39fe65403d8d78c764458334141c46098a899d565ab8c8ab1a7464039f72beeb4f0d3b43a69ee063f31881917167fe6e884b681bfe7cc8c03f916b0bbb345ac36c1fc924decfa8ad613bcb3579fa1515ad96495625e259119149aecae93661d32db0d26b051a1cec2e16d984373c5b83ce79b28bb431097894ce01537dbb30c4ad642b7e817b4d4736321504967b70f5c4c8e74fb9c483822668362650dc143325123ce4701502e550fc9c5141534b23240e092287ced7dacac60cd97c01f2d71516c22845b39a943b11264a4f98ac5864ed4e05da850f9336eaa8aab668bce4beef982358d24edb6639d6baeb68c1c37534dcadec3666b18c8440f6eda6cd3045b7f068c193be24b54372a97391a3f143f4b96fbe4a19acb20694c3bc69c2814abe2ffac3514ab6e7371c22025fee43add653981c9fa37af6bb30f27821e4dabdd6d847dff803decc77febda719cdbc1abd4f1002b81db9eb3494f20c1c561100d6babf1e29127a681474a8deb6c44db25bd82fbe9565a1c53918545e07c12e1e9cde07243d5d6e0336134d216dd5b29709cab0057549133fb6a9e95fcdbebe08eeaa5486275870c09a205ca5c32eb7322b20b9bb81a2064ad4d9c8f14c723c97968e319b844a47e81944fcb682cd8edf1d597a4b98a3aeeb029249772b9bb57d5156acb1d1036b3ed700c9495ea285ae5a82d5c788b172eee610a6a1cddb31d9c27a77614b8939f9e014b017580cd6857531bb6d1099e2f171e4a52c3d119addb6816cef1a05f4ec5d9a662dc3d3147effe9391cfe59bda97baa89abd69ad986edf4eaed759ec0913972558a840a9b4fb87dfc5148d26340b61039c1741f87a5e4f858f03c316ac6d5a24e9e42841410adf128d25503a9c3334086a1263eed865c50b7336fdd52f76d72e2a07fec314b353af032bffea9d6a8b74a7b1d3a943c5ffb733dee16bbc179f5192264b5fcd5424a10c3c3e2fc281cca8084b9572c48c8d93e312e3806dd6e7652eb6d2a2a615c34fd018876287aefa6d40f06d321f0b0fa476f31f327a2d80e28162b14e49059b8e6b77577778f61cd60e57099e2629bfda09b44aaa624e9e85e9ce8f2bc9a8b85db28ad19c890f630ca358e4882a1485b0564ccde5028573bb92180ee736ad0fb66e5d02785564c817845bb4dca29444fd7a3b162bfd3bc01b658df1eb35bc8aaa13dd7eff92d4c66a7bebffbae5e7b52331631c0eac829d3b7722949011fa7398d973cf1e6bd863851452a7663ab3da869f6daea5b15973226a4f23daab4d66235078366a3532a1b4285c93c2d072bf1e9ad9b256a3e4bebf2ce55b46c885e78a6c9342e5cb19307b51b5428633300aaf7949570fa37aec730fb0d8e7aea7bfee1d54e13370c9da2e51d7a81c67556a9221175a975dca9728668573e1bf6f519554def0d12cb3a1495f36320cdeacf75cd9f6321c963e372b5b65e7650f158488dfbbd40dcd438d824fdd1ef62318614b64480a822c21197080d90be39520d1b5b59ca1f555d683174371776d7ee08a99679ef2f2a55af7d4e77e08b701ea0c9faf28ebb801067cbc708e951f2621385762f00df797e802f14366ed9aabbed3b5ee3ec0b2b74b566803619a6282de0bb0dbfa16faa4113f90d317ef331f2be8baebc5cc7f301068ed1e7a32f514948c9219618797004b89b4aace04714072ab0e5d57f0a9d06bdd6211811eaa2bbbf76d4ee6200a8afab8c5dd92d331c123b8839cf39cc7cf457f0bf04e35928533145c0a39c562b52f0fdf8abcbffb8bfeb8215fa58f797c0348c8868c876552aa3894eb34807bc62accd2b3f30758b081298bc4c3d4c9ff927efedb828bc629ef622e2edbe178ad70532d081833ab8686f4b5a3e2f6046725d33adc021060962a6be6e410d6e651f75ae03b870a609200c11a549233164ee4b5782c1c6c88ecf8d7a96ef581a4a0c0580cc5c441962ba84ebe9a415f159aef0f78574ec2fb5f0ab295b792b89aeac2df77b3c743c62584b9a119afe81d960c43711436eda67435288f1581727586a29d9c1c98ea945c64626aa7122fc420dc9ecc80d5e36fc8d71f8311b25acd3d11d7bedfa85796d4f02a5e9476845c8806ceb18900902fba4cb1caf338330e465c966120258486c4b3e0aa32b78e88e9502b7da265e804f1f881f81fd948209ca06554af60b631991cf8225759d84d453be06e479a0f8d13186ab504e4de94986f469b97fee6cccbbb54e68336177fdbe678ba93b5bb503516474cb98e4827d20b60266439bcc65b70ed829b45b05b0d7d60da7b099dfdc3cfe5236794c2365e1f655c5e7eb736f761eaa671371687f99e213616a0445dc53ec49144a26cf8041a9ae74772089090fb27e0349b5bce7cb9d9f054e9d5e72a8d1d4581edfecca0dbbdaa3a1cc8fee8b2fb90e25467ec448fa6df4b05fdfd427825fba29d4c718203593f9259c0c593c71a00b901ab3675402f096a748b2d6d66d3b642a9c9f12691108578ea4360001376315c56b99a87f1f7d9401537052532e5b4ba0778aa98f8fe599c4aee56f9e5b882d64e33f101488e10f5502ddeb7f0bd6cdcc6be6c72412ba4e28920bd20cf3d72b805c76b6bff2895bd13150b163587a152c1e1664ec2db9956a87ddbd4471a3c1bb83583b2f93ab7e8eaae739663e45b2354d127346c08191f006b1e67d1b5a7f6714c5abc4bc3a22c5fa11d892a7f1d5fd12655145478c42216c66401172a196bf689181cf37439f738cdf0cbd738b03b7be21966a8a95626eef8de15ed1c6490392090eecfe9930c247eec4ef964d4686ba58a401dd97d1ec2a23b02919a8772acad453c899cbfb88182df74c91b6cdb5265b96b2f4c7ef9ab0635f57803b2b5dbea2d3592c70b2c4fa88a9127c780845d0e7d930565dff12e9eee8094dec44d0b7af5b4e841d5f1da0be424993eac8d405159c1168fb98fef0928ef4a94806661d37e16417de158ce9f52282af51791e78e8c2e0e935fe5151b508a612720eeabe36bfdf7c04c34bf1bd35331088697e53e6b554dc080a1739d55faec48ab7430901c5a37b5c61ab189fe3deb0f2c781d231eb57dfe457b280a2aaac141a27841a4e43973050538062513f15edf39deccb4d6c87791f0475092bd3c5e91384984a0ebedc37daf8e79c727c2f8bf8ef9a79a0c8144dfce77336ad85750e54e9e71bd7cba0ed50866c1d3bedeff85f7b1262be6d8cb2045ce905861dc5fb3b5a113b4253dcff5e9b44f94bbf79af7c82fcf096fe1ccc48a6b96cd5e7fdd3796b87f14c0a55f05e04cf890fbfd4a6388e54d97c622dcd7dd84dd9c1ca288a3fc1654f7d33d192b464c9ed3edb1610bbc7ec142f861c925ba05dbb3e1808320a66553ea320368ec8bdc5f757aa890825673e9d0e80b6b60a0eefad5bdcee3c03ee70db608d24f5e0eb1c64b6ba92f10b58be246655b984b58bf5268504801b4c1cd40c633503d87b907452c332ea16d7be122fdfcf5ffc2fbc950afe5c65f9fad7db166ea7dfe250aef83f2a4da341aead9f40d780fa4562e3e06a47a1b271027add92ffd595638952d101332245d0b94d1ca6bd726d29ae1b495f01868fb79e8505e1caeb7f8379e689add14e2f5efbc750688f4b2f76245efb8addd2458a7957f3829f87138e61370cde7ee72eeee9a0e76cfa79e6d869c3f2e33ad0ef7f6945af98c8659c936ad10c9f66a639c33965a25eb68289e473243c4a8b58132eeecf7fca073b341c6f8e51d88e955ea4b59c5993d57c0d52929d03d7d24504aa6a8865d3766ca9fb619bceac8214c4c6b9bf464493c534c5eef688bcf9538860cf14fc83c4471baf632eb4f8f7ed163fe80966de5f5e7598c495a0d62849ff611a6f308aa6e787752dafe0ea3df7826488ef9040d0b4f05c199ef9e7e2bc39740830cec27e59925a2922f89ebe65089eca31c1f366e300cbc6c239871d7df51975b91a4330850dbde552ef715793df19e8875eb8b9427ee44527a2fb8da0aa347d4305549866d16ff7c608e63781d80e5ab607ff8472bc9d315b8ec6ef1d3e027b9a177a3ff565d21eaa9931aec272136a3c70dbd2335b1bc735f93fc29dd3bba5c5d32f4868f9296436d057756530db0a65b632ed0cb64da888454c5322db6aea62ab1441c8c21bf406782e5b95ba75b6d8e2ceb6cd6c23b9a75de6936a6c3afaddddb0309ea7713a5be55522aacfbf7e07cb5f2e45081f5bf4869fcad36f09a4cd2643a0145972276f7b017c3ea656cb3263c73da1c1ea1c3908ace4a8a0f630a73f0e0fb8db2de4b85c650d9c36edc0d893a0f0bd3c354bba131bafeb8898ba1ec24974d0d5d75b82dcb02fb2f29359fb37d57fed7a9114bdde0e4ece9ee73d4317faef53babfe386399555e41f7dbb19f1b1d076a4373506414e254fd6f2985026ede5774425f82749765337f9cbb20d034778484e0f99cb08abffda6a779adf6097c61ec22ed7a2c1d028ecebf4894db684804ff822fd4efbcdaf5dbf276061faca150e8b9947bb7bd849c10fa8181551706b21421f5acda536144c6b8daf2d3e7ec8c3b5b2b858915b7fae0e188e7df5e6e90d5bdc8cfce90987e01b2bd945c8b2770beca31eb77f393bd6a334ac7a40eaae47e0a675036e591c873fd2b8a78332faead69673db15307e7e23b6eaa55b444571fd402d873e445983c501e9638039fc8c5a7368e7aac86c75fc0396a559e563e733fab6301059931aaed3d97a22000c30ea9323a9d7f000de74d1e578204ccd73f26762def3a8edfff9b5c631ae52a37f4a96346be9ffc2f2da8a9a2ac20fa7509d005a633fd76b7512194a4b8a5076b2f01809ee0543678015475ee4144772b8d2b920f52eeafccd2a7aa8e9b6a6584fe2ceb571abb8f0c9ff0117e6a3e322024718ff2b73f501c4520f1a43820e8b3ee29332275ef68aa602da6a5cc127870a773217642d56cfa1e90e433ce8f30c173f20893c005cae2e2c6e1985974cedd7ef6c07622a0a1d92bf6c1ac7cca43a0c25aeee2f17e3f99d275c6cf7c510bd34cc6a20f0273e3a89a98ebb23322d7fd1ef2c6bd3fc8e4b4e6b130b38c7b343009750608bda2ceef3f764a28fa2f00576b41d2e79d34ed312fbc5351df796f79e7625066cff9a4b1b739df8cacfa7df141ee7086b64dc342808b77f4bfa4f5f20aa1726d085cabe5c2a76a6c000118ea867928e07418600087b0c7a8765a8cf0aa1c844ca5adf6a109cbc8c33502a03b2e339a87473862c09b749af5e83bae5eb411f33b3558072891bac0ab41a2ca027ec82c00d71bab18a2f0b4c53bac438b48f6ed0084516e21bc9497adae3415d180c25b6e9286e58f8697cb83540b3f3cea1ab20159d3c5b0d4f69b888562096639e4483971c5d46631939de3320892ad2e28f567d84cb7127cb2ac86e2cbf80dfbce06b8df580d28c574a4eb82f5de6778cdd8fbc7bb79c4a3bac618c0b0abb1ead50221572d9a60333a26".to_string(), + genesis_string: "Concordium Testnet Version 5".to_string(), + }, + ars_infos: HashMap::from([ + (1, AnonymityRevokerInfo { + identity: 1, + description: Description { + name: "Testnet AR 1".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker 1".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c58ed5281b5d117cb74068a5deef28f027c9055dd424b07043568ac040a4e51f3307f268a77eaebc36bd4bf7cdbbe238b8".to_string(), + }), + (2, AnonymityRevokerInfo { + identity: 2, + description: Description { + name: "Testnet AR 2".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker 2".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5aefb2334688a2ecc95e7c49e9ccbc7218b5c9e151ac22462d064f564ffa56bb8b3685fcdc8d7d8cb43f43d608e7e8515".to_string(), + }), + (3, AnonymityRevokerInfo { + identity: 3, + description: Description { + name: "Testnet AR 3".to_string(), + url: "".to_string(), + description: "Testnet anonymity revoker 3".to_string(), + }, + public_key_hex: "b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a791a28a6d3e7ca0857c0f996f94e65da78b8d9b5de5e32164e291e553ed103bf14d6fab1f21749d59664e34813afe77".to_string(), + }), + ]), + id_object: IdentityObject { + pre_identity_object: PreIdentityObject { + id_cred_pub_hex: "8eab3b934c6da9ca049ffc345455f1f0ef1076186baa00bf62d69ce0b20e25b2c2555feb76da06d0b9999fc1077f2bcf".to_string(), + ip_ar_data: HashMap::from([ + (1, ArData { + enc_prf_key_share_hex: "a508c8af8ef201cedc8d9740a1ede842a6627b2062febf16545689dbf29d23c2dce3043a0646a780fcb3133bc08711bc9862e2d22d02179b44a65a2d19d9b747ad4a03e11bc9d0e70031f83c9b6e72718183c54d6dd3c3dcf35b5f8b76ed78bd94bc6028f06c7486e31c439016b80d84dec762cd24597c96992946710d0c945f82348833bfeac69a0eac7955be33555692b6dd6adef8875c438de19c62b1282e8f1d797135bc971cfd0d1b156b22a6a329d51785d02514f209c8c29ad6e1cd0da42cca5bf6d710befe9b8a05d16eda4f2b99210e035f2a9cbea62482b3d438f0e76dd6e6117b947be723d0db3deb02d1b215573acd5ae403854d12c564076854f8ec000941934233c953691fad9716ac17f48aff9d44dcfd77b97741c616715298e92f734dcbac055e00605cc501224904c01d566749b222500d427a369c690506da072dfeeb3fd0704eae6d0836b2fa980ccc8cd1d6f307e9507889271c3a8a40ba15d8143d49a286be19acba969ac76207ef8181ff46a524fc1a2c6b0d2e46aa2c203c2981552b68390d4877f51caad73503f02db7f44656b710ce8f850342c86c8d848a00efc4b6bc390a61c1af2691623dfb0df387eec5458caeecb724d6ff3c5e93070557a89bc1a77ec3f6cefc1cfd77ee7d6d177476f1ccf07d31540e933ea3639e7f0eac6a07d6b23f0e22a948cf47c9282dd6445caf1e079b74e472fae556a46753cf962e613b32b10cf5049148d0ae8f098748401dd9f7b1a65748dfd4d7ef3ad660b843f7235a54c15392fe182ba9b3df99174d33895c889be82193a3c32dca44de24b4939bd120c53b6eb433c1fec767b625ff3af160e1dac6afaed172239c6a269b0a2658c881c3ca0296eef0a50a8a7cf17e3f4ae655918111dc77f215656e07e4d7cda2fc195d162667248cf0aa8898b648f254e7234712b0881af83cc54634f6255c6bc6bcad892f5e357fd9e68e1594468a1bf26833be30985fe9c66e8cff8adefa703c1ef3877baed7c0bbcc40499a01f91ae4f6b32f025083ceba279e910b455716d3e872e60b8f85867489841af66891dc232ff12a8d".to_string(), + proof_com_enc_eq_hex: "09a50932e4cd73210300967aa0b297b2f208f68e40fdf506ebb0a34ef621fcd92f18c098686c953abf424a95f4dd0378ec8a7e22f76f02e4bde44bcf35e0abee371e33b384a87d014697d9344215b181b60778b9d8db593369f0240f22ed342f".to_string(), + }), + (2, ArData { + enc_prf_key_share_hex: "844301db9fa390eb00218c8e4b5d6fa83a59462867f94b7897cda31a7b056653ee5f246bc665ac310cb01063ba80b6c18da822ad4993d5c0c950cd5b854fc5d560610c75e7d950d87e4324b68230f6c21f43062d98aa743607e7cdbb32b5a3d6976caa77ebfd8a1b101cf29ee5e2848b2b951ef4b53ff2f2dcefcabb3a2e65abbb58346ed4f0ed86c6875a64464ddeb6ab1b59f22937f3de049f8e5785bd27619dc73674b08d9b65d0cf26479d84dc68631582eaa6b53150b0d0c9f14352af1d8379f3182a6d3d2c858a77639e0114f64633c527e1e934d4cb52cd0b1278451bdf2fd5e02e9573b6cad1d198e0804efaad88d0fd36dcc79ef9bffe99fb8837c9b3624b6ae43324c03730393a0cab0f939183497e38ab2d0b25b5c816bff3f204ac42c0d8e67ec091ab9898f724b9e994ac5bb3c7dd4b1645948f366ae4051cc654e9bd3db2a99138bda90288879b53559387962b0e21c7f733d51540d6c1a7cef9bac8362c6352029d1bb6cf3ffa8ccc720f107f4016b8c556d8e1c891d3af13b84b64073ea2f5a3f6232f1e1df5466a87c8d1c83d75b836c84f64b87e71ded76b8684896b7316e40bb741410619478d84526add628ee76e92693800752053782b3fad0ed9853818c3f800fc11ac2b6e3bb10c59bd20622116f842dde694352db154fc95c7153c497b25bf2a1cb07baf5f41354961c970c85eb05e6b73197a30f4cb538c5de64b91b5094356a9f1d9c4944f17eec47d70b9b8a2d405f8977b051975cfb30e493420431e1ac5ccb5701d9815bc2c2b8a48afbe86b9495fd691f6b22bb76e5198f69ca4f7bbb1f36ae5db45ce3e11735d3572fda43a01c62518de8f13078f063af06dd1514800800e52b2ad428f1b76933c8156a41ff7827bfa779ccef8b340a8ce21e13f4c7f6fcc4980dd960b8be5f4489b89df1cb065479a56826f182df7d534d9c6d308dd9c9f2c50ffa57f2b188bd255f79d212f984025587f2bfb52cff71f0a7ff31d4fda11b9f1a77d05203971e330b8f468696330c5eb447cce978cc878a3825c6fe97b7db1959105710c4bbd691da68bb1334293200e".to_string(), + proof_com_enc_eq_hex: "2abd24450f4d2d11425f9d6cf2c5e99720c69473560b96a4d2984b661a8d7ea9238a4aa078b95bb63fa88ee52db87e7ac4327848fb9ec7ca71257be2c922760b6703e43f18534d628f05f7ed5a7caa840b23ffce8df95cf58274f8b9d2c7ba57".to_string(), + }), + (3, ArData { + enc_prf_key_share_hex: "97eb5b894ff5f95eee58ace7b9e0261ac50d96853d3173506a8c723009379bcfb44c6e769ea3006aa89e27fad5a21867a3e71b7ccc56cdfd1783de793e5a40a6fd694a6484afdca51db187a5f41b4dcf39cdf07e8b7d8f0b07d418ae6a6fe66890bb998f70924f1eb5f6657d55c592e48e702f570989e54f114f53a184f6a68983b6c3aa5265a7549f173163ec0d8f2698fcc1d9cce44c7dfaa4d598fb2411f9ab7f8002496efb219825fcbb74a357249399be609d18f69808209e2aa9938caab809508d699e8ca1ace70efe227b5fa77e221e2b4d389c344055ed6f80358d4086bcf3baec33f17d9ed042ef1e9011dda9652bc5a6ef66d71775188c22a537dd076991ae23bd9cf7d4610c7f71c66bdda3cea708d00bb0deff0e98e7c150374b8c987cb3f99ee3c0591540fb9b1a1bd5bd6fd9051d266f678fa3654f2050a2f08b7e4925d52222adcb6fb237944439a8961fbf061a5fa659b72348f77da7ed3befcf5e9633fdf4722122d38cdd26e734dbbb4fe5948f67f77fc0cb4e707f332b8596dd8b17c09adf030b6af70c33832dde308198986a64793a069d862046ae8eb7dd977055e1e4c16c04bf98c868aa9eb048c2391c11868e5e2b992a83617ebff125c84f03af26c6322374fb9b3146009e62d5b2d17e96b9f3802c4657b56bee8a63281d048103aefdcf8679047596880c25cae147d1c9c5bda4ba48408008d1835168eef23402644f3b002532e9ddc79969fc10ec1788a658cabc1e1bf7aac2829a2f0099618680273466a42adf833ed754993a1fadad109ba60822bc57e9a58467e76d7fa3f082ead28ea6c6cb1a238889fcee69328ed296c0aced243a6ee4a7838d5cae0f5bb026de613470ce5259b22e249caa0e0d55432e7a3a05b96a2e53ef6bd87ff8adadcf482f2dd17285f81c05160aa334f168dd1a9d5d88f51152b7a6987268aa0464138741b9ff5a44a8db22c99fac326fde734ef8620e3bfffb748af296a5858f4c2e553184f3fa5808a2e4646bb39c0b34b1779e48a61d608a16b381308a4f33244cee4545e1e16abf8b56dbcc3ac6e6515c0e8d4cd15124eb".to_string(), + proof_com_enc_eq_hex: "2ff7e67f5fcbfacd63b6b64bc0821f8a23ecbebbdbcb6d6cd6a650872cfc886b1d1911bc3e4589c4a344d29f01319702b2982e60678de331cb4dac4dc7803abf00b0924d40fd945f721582cb5f4113e4dedf1dc2dbfb14e21d99cbcf705250e2".to_string(), + }) + ]), + choice_ar_data: ChoiceArParameters { + ar_identities: vec![1, 2, 3], + threshold: 2, + }, + id_cred_sec_commitment_hex: "9768c721cfb0ab398f5cfc58c2674612fd78b6f6d5e5a74c0ab7c9ccc27aa9243bb3937e03c0c7643de7325f4e3997c5".to_string(), + prf_key_commitment_with_ip_hex: "8325e1271db9c64372bb3461ec97a0c74f0e83f7900e92197a809215b2a50a1212a4d5b6553afa51ed12cb48df22bd3b".to_string(), + prf_key_sharing_coeff_commitments_hex: vec![ + "89383e07e7adc9eaa708aeab2974fc976c0cc238268c00e49f1dab086f8db280e93b00ecbac833913e47ad4bd912eece".to_string(), + "81096c2e2b9173c6ddb55d7cd4f74d4fe6e53da1d943774d018e46a792625c670bcfda82c0980ea6bbc9b5c8a5baec19".to_string(), + ], + proofs_of_knowledge_hex: "a077c142ddcb9fa88786f5ff45bd7e8c5574b36f49505b570e54302dce7d56fe0b86bf75c4f8ba58bc9299c28fbfeb3f6000a670d3313b3e8adb3421c581e8b223487ff7b35df259480d5cfc48bd8579e3de3b1f95da525bd2e340ba56f1871e1c00e3b43f637017b7aea59400f1fc2d9fb1512e5ef3132282f58044b8de257a36864c09ce2f97dc9b83694c6d7491277a1b30df8e74324bad4a5ed0fff9672c5f0e42468b751027e2142e5cc6ce52ee86acf293c8868dfede855d4b5014256b48b62ebea40b826c20bf6f5ca669829a801c51531c870241a5088186d84b73540000000000000003b88c9346c38a84d52f06fd1d76f4423f61d555edc7cd583cefc8c4815cd02ffd5af54b1b4d01dbfc5ce72e9572c31855ae24c04dea1ff5b61f4c50f51ece9d3128b7cd33adddd76a2b8f781a7005bb3133380051d0c1c82d18e0944ce88f3184900e84830dda1467aa434c539c91d87ba80cdae54902ed844c2b899d58273cc840041128074b751f7cd83f84557c77e8aec8511c2b55a326e20024515216d25ca9ed095eba1926e61b2a0165e0ac92b473095495a539e93b8921c788f3ed47fb606c860c40429707fc648bf81b66b4354dc325af99ccd70979cc654fe53147b022d47be9807bd8fead343e0c2d9a254011f36678181dd76c9eb0fcc194f8b0145f25c2a3ee9f5c974c0c37a4605607e83a8d8ab769eb77a6a5b0e2672554a26c00000008a8c95c9b6610f33f2c737cc3c9e454e79f0dfa39b15cc31732ff60ae391eb06ade26145b0c94fa30280a4f64d415f5f7b703e791af91922e20da32f724aee5c4c20399ab33146924706c5626d8fabe1100bbf65c9d110205d1f07d041e04428e94fc28cd3cecd89e576dde546db80642a684ab4a06ce3dc45058899b496cf62b0d6bd39072d9b64a5aec5bd03c134f0b95ba5fc5311ec937fd63f71965015341182e001f61bd930d4fbd950760bea62485eb98d9e52475973f652f2cdfefa74bb264c19582d494d74a1192d0a4bc377e8936abfee2bd8fa1f6107780ffd0b03d6a4393567169fd91f32782f0cec2308287a8346cc3c4527a16394643628d5e2d3b6c8d1529eccf5c31fc59daaff1df1d867a21ae0df03507a0c3fae5ca97b18eb32aec6d913534a9af2fa6da0785a38a3b74fde421612e2432f5c3c55400868a45e9845609d8360c72565a654c9d3c8f93656e8b56615703d5302b9953ce1215380e47d5f86acf015089e04dab47c47119d51416dea1555220b96d8aeb4d7af5af1d3adaba673cbd9a205fbbae519a7312f6197390d493bd654d668cf6b3a576b7960baaec77c6ba806a284762f2ea8eb524389d790a49936c19ca927f858aa2ed66c9b685bfac24d54d45ffc2a812b2c2f320fa660c5488131cf0dba1b191c684a72974d2dbd82536575160e976fafb7c308f6536e60f7e570c6c00acd0c7e95ddfef4ea69c1cf1188196f7809bc4eca73c4a6676fe6937b0017834a0213450991b743e0c668891cad6f48de3b53b452d4cbcc6d1fe5537a0a23ee006af38ccaf506b51092cd47995225a36ea95898cd739247caa3edb82c9430ca006a66642f8ae899fc30796ea1101f65862ff99e985063a37d57845f806a6f6e97dd099104d9ea8e7477339c4acc8acc5e9b9f2e5e7c99e9b61e3666106cdda8297f06909aa06702894d29cf369876488b762f0a0de73bcf1006b770452284209002e6caafbf3a97d587d95a3fd8df92508635c83b7f185bf6efbf50c59102f150fbeb21fc1f6398dc64d56649e331ebd7628d5469da494b504be8e81166584786feec314710dd70edc0a3b44374f61add21b6a758b9b91c115d1380a738949829ac3433d40d940534ce1ff6162fd909b19516e732aa2cd37ede06fa456c7fba0d9e66cba9575372586e1840e82a0bb23d9d86691261f9c626d39d30e40763a2d3a7c7031bc47787561f0842981eb0de84ebf05d1a4c4e748022045cbba697a5fb65f3e6d0d62c67b47c3c6062363037c85913636fceb1dab97d50d5b5f4c9b9692aa219ba8983735edcde31d81ab8b23f4af5291ec49e6ae5991a73caa95a9cb73edcd8d3546dbb81f247351137dfeb54b8ed83cab7bb94c88ba393a44a22e809b5630a1e9e13c9e691e8f5ad0f1e49d7a2c7a1874b53fae814d4332b4b50915a9487c1e668c717b4d1e23f2df80635935b990288496af7e2eaf52ffd29c09c32de8227469f45de8e53de7cffb1e23060de4537adbf022f8c49f0528625825ccd8d2964c110366688f5c3a38d6cafbf67afe5481f5171dd3e297ed3c733d46df8cd97e350000000898ed8f04176efe36bbcd8f421ea5cfef33478b0c8790b81d408a6928106c94db87ffd9e5d170730d5b4c09a655c7679198a1885b509b9837bbb204098dfbf8b543c1b0c6e7f43a9d90f907a977c0213cbed8f8d581ef0d3add8c3a42d5fbac00b2f897ac5acb24378873ff02eaa0b766e7ea1e54b53db7f665ea783e2a2741a61dd3e7d5cb4e8c9dca21251728c7271c933b296f9777433dea305cad247ce5dd412ba05fc1ad2323ec5c9a19ce0498cb7733b3dc49fd45f060380ed16b897d33b7230b0e02ac9445b1fce6b45a271e048869aff15768d4b6fb3eae591b5c3fa9be3065d3fec58bbab12e2d5dae0042508bc038869d619f2ef87f3fccaf71286ae7cfaa85f42afbe7c334fb1c1254f13503535a7655f952ab04de77e0fbc2ef82952d46704b3f452a79a0b07a4d65ed855ceb57781ba44c3076770ba58883ad5b2afe09b5acc9dfdb8f06ee9c1d37cb56ab7d185aace999e9505146c1ede1c8d7eadfd0c1c0f0105551ad6ec287c16bbb29b8c252e6cb6933a4f02ecbf979d951b40fdbcd68310d460dbe27a69c0ce970df397707baf37824fc9ce94a91d2b3e50e92a4d1743da45e4842ab8eb2e21e38968ddf4ac9927ba05d22ea2489bd177a24b28fb09f2e0322c05ba9a8171b1ff19d267fb5eaf85f451a8971f488a4517e95ee98e73fb99ed4a39e11dad10b9806d8ba0099fe7ce62e7465c056de6e7f9c9f94ce86f7ca7181ec3a36fa7b49c123b9ade1492afe58a58ccd2c3d2b1fafade62aa52e1603234546cd3de30c5242fceace0b7609e2f98b6b58c0483acdd14ea907ab1304a34ed7c2b3d64b701859d4a880a31794a75c49f5ebf4b36e6bfe0422260936db2942add879a64335853ee6a55ef19ec97a67a290ed07243fb5738256e2b6b4b839904f7ee92bcadaefe501002c234487881667b4da40606b22e474a872d533c203bf37045d72457af661e86b5ef0a1aa9b22622a60a860c02e5249d315f3318f072bf46cf2f0c4907f5ea2aa0f7948ed2ac2e23af8140689d6f7675b314ef85b4715e20e8db6fab9d9174bb957f871c6d0f2452f0f6f821b315dbb6dd1fdf168a9e56a9b591071e6aa8f4c5ac4d33b16dd918db906ba68be7cced2389f405a27e44762e4e5eaa6ce8fd357fe93f8524aeb072e46879c542bf3f530a6a385235a2ec42ddbf5c1b762c11c071bd7bdea835f09e1638213887125376fda3b2d88100b6a1d89f0fb5b3165c3f8ae35fcf176dd267dbb8cd40b8e1ff25420b21639959139ae3869474a6a8d50efc7670af54d05135f2eaa559c37e9540bb3f7ae895c4386865f221bc4e2adce3e31bec8c330b87866c307304d8389e0a32c433d93b52a07e0754951a9051e64469743ab32d57608e3bb3dfb2c969261f2ce1ac45e34f6b483419dcd41624626d25e3a1cdf351e5005c0d0d55ceeedc7b91300f738c6135b05306cd480332f2e483473b812876cc9a95c6ce32ec5d4cfb8172fc94d832fd01d098b6370d62b7d2e59a6d122abaf05799ccb19d7d8e179d14b5a1d045c53a21941f8ed45fa912562c9cce76fbaab7770715ebc856db7f449000000088cb652bafab2be8ea34a83713efca27f7974e5f93ee54df8880d4a5df5eca71afdbe465001c40a0a783a16320c723c01b36cf5390bd299369271f1846f9700258d7d60b32339c88feb2a7ae359f51e8e237a6e2ddca9688910b558a69c30896081bef9561f43780bf1efc47ddfcf82c2b326f3438ee9dea3be2541586c3dbd66e9b984b2b287e15c97d2d675d09835358b519d14274b43ebc540e66aae0fc4a188d7562047c29726b6cceb542926f171a816508a435f93dcd80b42cf07049b40826c45edacec7238690b8277ccc5ed647a55adcf7d4af3aba2c6f221f75249b84f4dec4bea5c7154edeedbcd04fb761993edfe01a6baa68ac43b89783a6168ac702cdf28fe1548140f798b133dc992c4233cd9c9d65d175cc7783fa34a5f831683a58430dbc483606642bbe85317059226f165724711f1daaa5bc25a435aa93352efda89cdd213d83ceb63d8950de578b2712049280723e0d3f61212f9976bf542ec149dbb5dabf5af1104746c192a31d59a828add2276ac7161ee0fb7209ab3a988817670e82fffacc762e9d0a92e8fa5ae68c51598648f57dee989e674a1423ca1c41e012befde0b3a62299d4b9d61b613b79ddf0bfe636a27ad69f4b4d083ec4425016a03f8b5a64964c1d4631b433394804b22aebef8ca870dbc6d24ff618f45e59370a0256a5e8b53bbe9cb362ee560dcb1ed4ea5b76b272054e947badcb6f43ded5841922a0a922038ff483370b5f619498730163df4e1cd0c5204b71bd43a7828ac85811457f596d17ff728aaaf7a4434fb0854246eb196843f08c532a710f546deac05b129b7bcc2b3434a4034165325c3d4263048324d7f0560098ad9d701a3210072d04a1da5e1c7660b5784f7474afe0eaef17e36c40226194e2f74c99af421d6d431fcd0e0a0782de6ce162d5889b37566c3bee536bc7a40a385a48d203b69ab7dc116bb16da299c728dcc398b082c2d732133cc6d31584ffa0659a7171ccaef8c3838206c50b1d9460bb54068b0e33afb8a5b1cfba3a8f417b2cf7ae4ee4fb93596e733e5c342bd874475891663f4a04eac1584da79dc73d83a0b6d510022ba13d56d62eeddd95f66e572869ee1e7e5b40d84df76dd4d7f5c334cfc74d50253914fbe1ad713bc4314e0032afc547038ed1fe9b4acd24a6f085f".to_string(), + }, + attribute_list: AttributeList { + valid_to_year_month: "202502".to_string(), + created_at_year_month: "202402".to_string(), + max_accounts: 200, + chosen_attributes: HashMap::from([ + ("countryOfResidence".to_string(), "DK".to_string()), + ("dob".to_string(), "19700101".to_string()), + ("firstName".to_string(), "John".to_string()), + ("idDocExpiresAt".to_string(), "20300101".to_string()), + ("idDocIssuedAt".to_string(), "20200101".to_string()), + ("idDocIssuer".to_string(), "DK".to_string()), + ("idDocNo".to_string(), "12345".to_string()), + ("idDocType".to_string(), "1".to_string()), + ("lastName".to_string(), "Doe".to_string()), + ("nationalIdNo".to_string(), "N-1234".to_string()), + ("nationality".to_string(), "DK".to_string()), + ("sex".to_string(), "0".to_string()), + ("taxIdNo".to_string(), "T-1234".to_string()), + ]), + }, + signature_hex: "a77f3750113be9d0f540629f5b9faf10e9eb39dd1e1a993461675e12f2bb259d810611ad997e01b60e2e2fec3b45be34911d96daeadb9a33906aa2310c0462143edb512b3e04c06fec997b80a838991514981882ba8894a799f22ca0a3adb72f".to_string(), + }, + revealed_attributes: vec![], + cred_number: 0, + id_cred_sec_hex: "0d47ea94a563608e0a3ee7b6a73e249f5fd9c744c8e0a566dfa0034d85274b42".to_string(), + prf_key_hex: "3940b72ff4d28ae22287cf95218ee76a0c398fdb211d2d2beaa79af8a042f409".to_string(), + blinding_randomness_hex: "2f73d7b1211cfe12ff8d3cf4ac4533b021c3ba499f61177dfb38dc74b4ad3bf2".to_string(), + attribute_randomness_hex: HashMap::from([ + ("firstName".to_string(), "115acaf68bb539f1902316b530b918b9a7c5d2c7f3491eefa1ec356ed7a663a9".to_string()), + ("lastName".to_string(), "46ee57d0bb83329501b733957cee346af2434a9ee6048552b92deb8ec52f5aed".to_string()), + ("sex".to_string(), "5e9d6f98507d2a6df56f81b19dd5389699c9d55d01296896a87dd1c1ccbd0896".to_string()), + ("dob".to_string(), "21066350b3346b6a0d4a3b89d441002314674bb5dc05104e5666cc39a7ddc822".to_string()), + ("countryOfResidence".to_string(), "223869bddd34612d0442af751b532a7f044c14b681175aafcbfc3c7b52ca501a".to_string()), + ("nationality".to_string(), "06ff9fe077f3bcd38342f2a1f838989f6562ae1f4c9c8b08f05d1e5c6e43c1ae".to_string()), + ("idDocType".to_string(), "0b58df78520badb9e6a164eea03c5bc489d39292619d230b54e8957443516e10".to_string()), + ("idDocNo".to_string(), "2b1d3eac0f74f6c85972c89b6be236bc74e04b21a5340602894838f57a59cb8b".to_string()), + ("idDocIssuer".to_string(), "3a7569ae903c58a625c6456bb6dab0690c06e207ae6b77ba2ca708e15ebbaa83".to_string()), + ("idDocIssuedAt".to_string(), "1583352bf8ba1a185d4edaa27d8cd3836ec9b60e0ab39fc7edd1d3463bf5e702".to_string()), + ("idDocExpiresAt".to_string(), "03d7047cbb5d34335a86c205a79076147393699652f75259a1494d77e3a5d214".to_string()), + ("nationalIdNo".to_string(), "3d6ec52e9756497d30cb72eec42ec9a7af77a8e4d3d8cc6663986ca25643f3df".to_string()), + ("taxIdNo".to_string(), "5d589ac0136e96cb9f8e7e02edfb66fa18c7acbfdd8845dbad881d2ed8632d89".to_string()), + ("lei".to_string(), "3b0e81e9fc8eb8fc0f7dd3aa3112dfe544f27ef395a04acbbfe77666a0fc03b3".to_string()), + ]), + credential_public_keys: CredentialPublicKeys { + keys: HashMap::from([ + (0, VerifyKey { + scheme_id: "Ed25519".to_string(), + key_hex: "2581eca64c0c480d4786c8878553f064a008b82be869af0e12149bdf26b62a53".to_string(), + }) + ]), + threshold: 1, + }, + }); + + // The result is almost entirely non-reproducible, + // so the test only verifies that the result was computed successfully and that all conversions went well. + res.unwrap(); +} + +#[test] +fn account_credential_deployment_hash() { + let input = AccountCredential { + ar_data: HashMap::from([ + (3, ChainArData { + end_id_cred_pub_share_hex: "8af70ed13186d7c332bb894725c99ba9e58405a668cb3c9c48d6e85029284d29a91d8da59dacd0d85c925659426e198396c3f73e791ccec217bc92fbdc618877544b9a2789b4ea89d4cdcceb7df4cdde64284b400374585a59596543ae6c45d6".to_string(), + }), + (2, ChainArData { + end_id_cred_pub_share_hex: "994686187e9568e1076a6a711068dae4bd663f6ca2886fb3f97254ff0cc4e9b17797fd2a19237d8ca94941b697a3d7308b9a8e1a12f3eb856d52d3bb3adc016ba8c8eacbe93339a920f436de86f66ed847a1b03aab3645c1635d956b723442c8".to_string(), + }), + (1, ChainArData { + end_id_cred_pub_share_hex: "a52beef5e3c6491fce7012a268ee5b3ed03aeecdb496abd979bcc60803d6d3c970112599d500690c2f447daefb81223d99de5fd822fdfb7ce4c9fe7253995c639f9037ad64a529393ea5cc7db18b74432469208e0076a80ed2c22f7848b82895".to_string(), + }), + ]), + cred_id_hex: "a9e510d6685f81c2d7f8a0177222d067982cc73699c5a7d64a80d06543797e808e04c52ea393ee300f50d5b4c6e87c57".to_string(), + credential_public_keys: CredentialPublicKeys { + keys: HashMap::from([ + (0, VerifyKey { + scheme_id: "Ed25519".to_string(), + key_hex: "2581eca64c0c480d4786c8878553f064a008b82be869af0e12149bdf26b62a53".to_string(), + }) + ]), + threshold: 1, + }, + ip_identity: 0, + policy: Policy { + created_at_year_month: "202402".to_string(), + revealed_attributes: HashMap::from([]), + valid_to_year_month: "202502".to_string(), + }, + proofs: Proofs { + challenge_hex: "f418a4ae77726e04703cc5e7db08b69c649f8f76f903d4589fc50c2ddefb529c".to_string(), + commitments_hex: "8067a3e8f17cba6c29fc66a697b69020e8bf3641ae35fa61d7a354c59f5ba83bf69aafd264f050a034c1c555dbbae047ab4c967385787ce6c1fb3e594b19152126255c06a53064ec39dec4f9763b2c06d2272b5358c1d4a6936d8d7f0910072d970b0b5512107183178a3b498351d8d7dc108a516fbe0dd783b45d566bd890bc0ed68e85c921bf6276c22dd1a81743bf000d00ae6563d450fe38afd17b0ab55274e8a3bdd02f01e1cc750166bce3f765d6225c22d32b5fd8b62b7f217f3d16fb94007b01ac5dfe414bd35411c6b9f01fe042f13c330e2a7bd756ba22c50df93b89e23b6c65802f7137144b0a4d47df6fb501e08002b2861ffea2f45ee63aabfb3d8aa2363b7bb58c80f4a5617dec6a6fa1cf5c23214a9566269d43bf8328b60db9426c470503981b672bf25fc45fb357b75ad0d17e104b226292521ed974d550ee39ba9bdae59af5b3b5794cf1598e2aa825755fa4bf04855e7d1ce5dda7b619c61026693f7c0e50eca18bfe4b100655e15eb6fa32f3413d971b5db5943e455f71e3196cd8d244058c494dc6eeade62ecc17436b9ea2a2b51e7bbcd0cf872d1a3986a3371b70b2e44a35d7c51a4cff7648ff68a7ef89f95e0680949b1f8d306ae0d44bb7e91c05a3e42ae40b68a70591abe06fa2e5acc769b489fa311e508a2eec7d015123181878d40788efcf20034fcdb5f14e1e41f357889b9fe7a1944769b3c2e84dfd11df406b7703d436317932b5e934a4ebb0bc59c6c508b751dd27e4d5413f183b8292b60da20cfd734f6942caad81c93a426d25951dcbab33254e0c86890f8aca0318197d938a09aca480cec324499b5fa291ca13712b20e841961f1abe1d4182f465bcbdcce77c3a52c409d41e2eb15819814d59d0be660aa8b7085c0f899c1bb1f44995869122433b910b5e8f90f63de69b9da6782bd72cc7a431b1d94d81d857bda28f649c194f0bad0cef63ef68a6998aadb1a59e11f15e853f6293a77d232d706d68b72d32ff4bb685cf91796447a10f06688f45ad3bba0ca0549009a431cbc581bb751d50e0e898658f68b5d25759022d8e5c05eaae18cad7e7d7746b8ea3b46459323cc02ee50b0000000000000002b7a9c414a294bf75a81826f5480a62d5fd7d1c83e385c3802e8889a47db1e63a5604e4aecfc5fda60127c60dd7bb1045916ae3e786c448ff873113f9f10d61cdffd1b85b3ad3bd89faf2e6c00b50d2ba0839e8a5f3816e171c89a7b942c0f1c4".to_string(), + cred_counter_less_than_max_accounts_hex: "a795ea39e70e07b36a6378ba5e3584ea8623bcd78fceddff98384f8e0e851358fa830682a0b47df17cd26060e372bf0ca84c5e346b51308e602bbf8df6a20214c688ed12cc061360fc939bc91317197b4064be67245d590fa92f8010f55edf41b1429cc8a95a4fab01d5a4d9781a2c04c9fd963eb474b2746f908efd224c57caccc42626051fcc21f2ee46df1985e412878bdcdb35aa9eaf17e5f3413e079a87a28cd46afd1b2727d7fb65825c3101404c466d5a8cd853cbb73b79c852eb3aa3666fda7d9a555d7fca5e1d22477b695de1eb8ccd975847f233d512d170d1109c3bc6648d273473a4a535d3948df6850da5eaa4c807eb107e0a0dee867a9843a348efc5bba02f31daacc066951dbec22f956b3e423f6ab42d660a899bea9a64ee0000000490bf02717fc3f1015f005604496a8beadff179d2b0787ab18baba7ed6061f4d91deb511b6ef3f9bd6596a726bcc8af8e932bf7c21f810e038346a21474deda3f1ad7bdf559fea924c21301d2f95200b4a04791bdd31d5426bd40f51cbea8c6ff83818561c024c354001cc9b654e118544f8498a8c75bc82a3eece23d32ce18f43eec6edddec81e1261948a6d9d934bc7b7db029dcbf7cf4211b5cdd76848dc56567600c9107aff5a4e63783992708ecb205634c3c8964af4c7cf97d43f778c628e0d6f52e999028c854c2ff019768a7ac8a701c795f5c2cf2aa698a9c7e11bf1697e6f9763ce4b9138e5a2d2b2610cefb298266c19d0b86f937a08ddaf0f90d74ae0a63a5a0b0c7da69f764e1597407ffd0a7dfdfea0e75ffcb8fcfe23495f8cb4099429eb0c04a08823c18f65f060c631c69e7b535c75192e3d7bdca1c83a9408d76e2eb2dc872197bab615b588633a8da691dafa08d113d169d91ff3dbdcfb33bf4ead45c68963f7e79ec32da43bdda70f1a9f0f8154664cd355b3088616005dab4a86e3b1636b51c8cc1899d4764608cfe57128d96dbd860deef8c44ea9084c2b9e5f7da3bdf513743b9e86b75205c43d46fa244acc17c77b64a238cd4a9a".to_string(), + proof_id_cred_pub_hex: HashMap::from([ + ("2".to_string(), "3aa278e933f551bd90e9edec2239278ae78b6d9afc76ac1244805331b7c569140d3134077a0c2d8ae6abed6b5caf89233988a8929cf759ca86e9a210811cae6670d40335cb3350d6511c308641ce3b4e40e75e406955f6b36fba2af9fb0bf89b".to_string()), + ("3".to_string(), "5e15e30be5b8b5ec834c650300a32f7f13b8c2f68d4fcf072a2abaae9cebd2fb5f465c56cbcf675610e1a28789926e41973b40cca7ceba46c04b1dff5f581ec35ba32f9795d73e8ede135699394073e5a26b0ccbc006a4141f692cfc6f8ed672".to_string()), + ("1".to_string(), "58ee5cd5135a688e7bdccdead68a80bf4d7bee23017c2378ee725d39b5ad0e3b53a09a31179ad09783e0daa1ff71ddd98a3cc9f352f2ce09035a87dd47192f713ea09cdc83fe5b9c313c5da290bb063cd362787427c7bbdb828f869a44ccd347".to_string()), + ]), + proof_ip_sig_hex: "2a0bc89c10ecf4f1cd2c9f7588383c514503d19e0755c555e0e2d7c3c0286e9400000013259dd0a487127867aa3d623ee0f37a584cf01ab630353a7ac05fc91a8b42ac371168bd89f224a382265bf71ed2d160ba070bec86aa97430275c2f17d85b00a93466dc751bda61ec2b8bac99f5aaa9c106fc7fe2d0c8511d6166ef0f1125611535a4dabcb1fb7c3eac1c0fb69ee8e5da963c1ae742086570d0aec418a5f4e33c24b75a17c0ce5a8b82254dca612fb1960c4003a34c71524cdaf433b722c6f777e2f6318fca19040ee8177c2421302273ca383c2d3e5e6481dfc8ac36fca9fcc066c0d7c939f850eb4954ca54091f960a7fb241c3d6e310f82fb3e8aca821d84c80e8dad2e2bbca1f3af28546d9c6206bc92b5733334052e1f9d15a27e3d6e42bc0e6500639a452237c35de5fd17621529f3353582b0dd67176a97d3f6875497091e5969f8a611831201706e1841d178469b892011abb42ecb2d8cde663379af2b5c3b71be2df105f24b79d4f3496e9036078497a9c2cc730e1d42a46f94124430058af2892184600d680603c368b0756f56695949ce5ac31c88afdb35b0bcdcd76d2081b73ed943a0dd0a540eb58573e017eef9552c3283da789b1607e545653064124019c01c3ee2560ce1edb15426ebdf53b7ab01801fd4e5223140d1bc09171c85691d782ed979dcae51bac7c19271c4dfdcc762869cd3ba7c6f7e2c311c3565c900a94d252d08072022e5af5badd124c7d6943795760807886c0608fb19f201b560c57dff479acce03e8f9f8ef78a67959aee83d810c32d870aa4dac5c41a6a30a08e04c150bb8040c4a814475c0df7ab1519f5a430ae0d08e7bb6af24a95140f4dec329d526128176e4f8e04838a84eb99442f7d93dc8fce2a2ba0bec01c737c765dcefb32da1b66fae19c74967eab6ba9e1286253f88042f2707ba4832a4140b8bf8a9c19357b3a5c25f379106ced2f1d6cbb90542843c9db1dff77db2b1ed6833a6886e80a90adbea68bd2937154ac860bebf5cc4d875b9f347af7f2a000bc2bc5a58cb15083ea1931938142fedb15f0f6c6ebfcab2f49f08ada620e246f84e1f1ef2fe23ce3a20596e7c668840b60759c1168be66e9ce30f537ad97a83cc472f9164bcd8c114147c26b556aa15f32a4e2a5ff02449e4006f1ab8b4b7e49973c6ceaef4e73007befd6396b2bcf23a63e310b049836cc880b18354035b805b2b887155f94d6136f6b6f9e41505c29251e82a247ab8b5fd624a841eac95f3564f94041a3f9c0d199d247275e4273c4652aa1df9576a67fc11ee4f3016c70186c2019f4e1eaac2f0913e21d810a382e12dfc1a2eecd2d23b2c2605cd38c4c1364246ccdac898cb840fde79c85c60c2e83ba1a7d1d0312998f30cbb1b1f9a9248de1abe48c7f43451e3a7c63568ba7edfa5d45585694d660c90aad128bcfa150fd13853d817d3167110548664e9f66f862b80a96ad25e1a18d7040e0b2af7452439399d94180df45c8bc6d3b273b8d94718a9b059be3d9a069bc8c26553b895459694350e475861b3295e1e7797c1b311aa32f7c02e42b055a841b248cc71067f45a743c89e59ec7393e5a46f2b4342fa7d2542df4e83994cbe9a8ccf9118d35400bffcf8ed4398e55ac7b766967f3da05c739ff471e76e909bfcca2ad7103381f7ec03515cfa8f6fcb54eeec47d3673dc76f2c9d17dd17f01aa31a340949d24fdf4612eea2e60b7bf3fe364953c6dd5ff707f390a1b8a4ddbfc2de4888c31".to_string(), + proof_reg_id_hex: "2a0162a32a55d7e97071c4d89e286d2e725ca0fcce1f53f6c4e3d09bd1b5f0941f35ea82fab665a0f3efce90f4ca2f16ec8707e1bf1b7df84e764df3bfc60af83db2f0e1771e579c8b062be59eed03537ed1e52830c809068075c173bf6809f56443fe6dfa7a3e00ad097824b7c186fb72e771106a7c84a2005ba3e8272db1e11d97b5dbbc51cea52bc03a8fe5384af339be77a1cf838ccf7de0a065805f7cc4".to_string(), + signature_hex: "8b57451744c536ed188e3140143b5e43db5d44fc1a75b484ad45387b5b9d4c39d88018a5ef95edffedca34f36885a475b5bee57c0ac1bced114a4f9bacf75f98f93ae3468ea10c4198288b91a001150a007db6e1d2477a8b2d76bf64193b63ea".to_string(), + }, + revocation_threshold: 2, + }; + + let res = account_credential_deployment_hash_hex(input, 9_999_999_999).unwrap(); + assert_eq!( + res, + "f5448faedc5011d1d6a622db7b6790d3ae1a06a331675717f62b66f9e33d1056", + ); +} + +#[test] +fn account_credential_deployment_signed_payload() { + let input = SignedAccountCredential { + credential: AccountCredential { + ar_data: HashMap::from([ + (3, ChainArData { + end_id_cred_pub_share_hex: "8af70ed13186d7c332bb894725c99ba9e58405a668cb3c9c48d6e85029284d29a91d8da59dacd0d85c925659426e198396c3f73e791ccec217bc92fbdc618877544b9a2789b4ea89d4cdcceb7df4cdde64284b400374585a59596543ae6c45d6".to_string(), + }), + (2, ChainArData { + end_id_cred_pub_share_hex: "994686187e9568e1076a6a711068dae4bd663f6ca2886fb3f97254ff0cc4e9b17797fd2a19237d8ca94941b697a3d7308b9a8e1a12f3eb856d52d3bb3adc016ba8c8eacbe93339a920f436de86f66ed847a1b03aab3645c1635d956b723442c8".to_string(), + }), + (1, ChainArData { + end_id_cred_pub_share_hex: "a52beef5e3c6491fce7012a268ee5b3ed03aeecdb496abd979bcc60803d6d3c970112599d500690c2f447daefb81223d99de5fd822fdfb7ce4c9fe7253995c639f9037ad64a529393ea5cc7db18b74432469208e0076a80ed2c22f7848b82895".to_string(), + }), + ]), + cred_id_hex: "a9e510d6685f81c2d7f8a0177222d067982cc73699c5a7d64a80d06543797e808e04c52ea393ee300f50d5b4c6e87c57".to_string(), + credential_public_keys: CredentialPublicKeys { + keys: HashMap::from([ + (0, VerifyKey { + scheme_id: "Ed25519".to_string(), + key_hex: "2581eca64c0c480d4786c8878553f064a008b82be869af0e12149bdf26b62a53".to_string(), + }) + ]), + threshold: 1, + }, + ip_identity: 0, + policy: Policy { + created_at_year_month: "202402".to_string(), + revealed_attributes: HashMap::from([]), + valid_to_year_month: "202502".to_string(), + }, + proofs: Proofs { + challenge_hex: "f418a4ae77726e04703cc5e7db08b69c649f8f76f903d4589fc50c2ddefb529c".to_string(), + commitments_hex: "8067a3e8f17cba6c29fc66a697b69020e8bf3641ae35fa61d7a354c59f5ba83bf69aafd264f050a034c1c555dbbae047ab4c967385787ce6c1fb3e594b19152126255c06a53064ec39dec4f9763b2c06d2272b5358c1d4a6936d8d7f0910072d970b0b5512107183178a3b498351d8d7dc108a516fbe0dd783b45d566bd890bc0ed68e85c921bf6276c22dd1a81743bf000d00ae6563d450fe38afd17b0ab55274e8a3bdd02f01e1cc750166bce3f765d6225c22d32b5fd8b62b7f217f3d16fb94007b01ac5dfe414bd35411c6b9f01fe042f13c330e2a7bd756ba22c50df93b89e23b6c65802f7137144b0a4d47df6fb501e08002b2861ffea2f45ee63aabfb3d8aa2363b7bb58c80f4a5617dec6a6fa1cf5c23214a9566269d43bf8328b60db9426c470503981b672bf25fc45fb357b75ad0d17e104b226292521ed974d550ee39ba9bdae59af5b3b5794cf1598e2aa825755fa4bf04855e7d1ce5dda7b619c61026693f7c0e50eca18bfe4b100655e15eb6fa32f3413d971b5db5943e455f71e3196cd8d244058c494dc6eeade62ecc17436b9ea2a2b51e7bbcd0cf872d1a3986a3371b70b2e44a35d7c51a4cff7648ff68a7ef89f95e0680949b1f8d306ae0d44bb7e91c05a3e42ae40b68a70591abe06fa2e5acc769b489fa311e508a2eec7d015123181878d40788efcf20034fcdb5f14e1e41f357889b9fe7a1944769b3c2e84dfd11df406b7703d436317932b5e934a4ebb0bc59c6c508b751dd27e4d5413f183b8292b60da20cfd734f6942caad81c93a426d25951dcbab33254e0c86890f8aca0318197d938a09aca480cec324499b5fa291ca13712b20e841961f1abe1d4182f465bcbdcce77c3a52c409d41e2eb15819814d59d0be660aa8b7085c0f899c1bb1f44995869122433b910b5e8f90f63de69b9da6782bd72cc7a431b1d94d81d857bda28f649c194f0bad0cef63ef68a6998aadb1a59e11f15e853f6293a77d232d706d68b72d32ff4bb685cf91796447a10f06688f45ad3bba0ca0549009a431cbc581bb751d50e0e898658f68b5d25759022d8e5c05eaae18cad7e7d7746b8ea3b46459323cc02ee50b0000000000000002b7a9c414a294bf75a81826f5480a62d5fd7d1c83e385c3802e8889a47db1e63a5604e4aecfc5fda60127c60dd7bb1045916ae3e786c448ff873113f9f10d61cdffd1b85b3ad3bd89faf2e6c00b50d2ba0839e8a5f3816e171c89a7b942c0f1c4".to_string(), + cred_counter_less_than_max_accounts_hex: "a795ea39e70e07b36a6378ba5e3584ea8623bcd78fceddff98384f8e0e851358fa830682a0b47df17cd26060e372bf0ca84c5e346b51308e602bbf8df6a20214c688ed12cc061360fc939bc91317197b4064be67245d590fa92f8010f55edf41b1429cc8a95a4fab01d5a4d9781a2c04c9fd963eb474b2746f908efd224c57caccc42626051fcc21f2ee46df1985e412878bdcdb35aa9eaf17e5f3413e079a87a28cd46afd1b2727d7fb65825c3101404c466d5a8cd853cbb73b79c852eb3aa3666fda7d9a555d7fca5e1d22477b695de1eb8ccd975847f233d512d170d1109c3bc6648d273473a4a535d3948df6850da5eaa4c807eb107e0a0dee867a9843a348efc5bba02f31daacc066951dbec22f956b3e423f6ab42d660a899bea9a64ee0000000490bf02717fc3f1015f005604496a8beadff179d2b0787ab18baba7ed6061f4d91deb511b6ef3f9bd6596a726bcc8af8e932bf7c21f810e038346a21474deda3f1ad7bdf559fea924c21301d2f95200b4a04791bdd31d5426bd40f51cbea8c6ff83818561c024c354001cc9b654e118544f8498a8c75bc82a3eece23d32ce18f43eec6edddec81e1261948a6d9d934bc7b7db029dcbf7cf4211b5cdd76848dc56567600c9107aff5a4e63783992708ecb205634c3c8964af4c7cf97d43f778c628e0d6f52e999028c854c2ff019768a7ac8a701c795f5c2cf2aa698a9c7e11bf1697e6f9763ce4b9138e5a2d2b2610cefb298266c19d0b86f937a08ddaf0f90d74ae0a63a5a0b0c7da69f764e1597407ffd0a7dfdfea0e75ffcb8fcfe23495f8cb4099429eb0c04a08823c18f65f060c631c69e7b535c75192e3d7bdca1c83a9408d76e2eb2dc872197bab615b588633a8da691dafa08d113d169d91ff3dbdcfb33bf4ead45c68963f7e79ec32da43bdda70f1a9f0f8154664cd355b3088616005dab4a86e3b1636b51c8cc1899d4764608cfe57128d96dbd860deef8c44ea9084c2b9e5f7da3bdf513743b9e86b75205c43d46fa244acc17c77b64a238cd4a9a".to_string(), + proof_id_cred_pub_hex: HashMap::from([ + ("2".to_string(), "3aa278e933f551bd90e9edec2239278ae78b6d9afc76ac1244805331b7c569140d3134077a0c2d8ae6abed6b5caf89233988a8929cf759ca86e9a210811cae6670d40335cb3350d6511c308641ce3b4e40e75e406955f6b36fba2af9fb0bf89b".to_string()), + ("3".to_string(), "5e15e30be5b8b5ec834c650300a32f7f13b8c2f68d4fcf072a2abaae9cebd2fb5f465c56cbcf675610e1a28789926e41973b40cca7ceba46c04b1dff5f581ec35ba32f9795d73e8ede135699394073e5a26b0ccbc006a4141f692cfc6f8ed672".to_string()), + ("1".to_string(), "58ee5cd5135a688e7bdccdead68a80bf4d7bee23017c2378ee725d39b5ad0e3b53a09a31179ad09783e0daa1ff71ddd98a3cc9f352f2ce09035a87dd47192f713ea09cdc83fe5b9c313c5da290bb063cd362787427c7bbdb828f869a44ccd347".to_string()), + ]), + proof_ip_sig_hex: "2a0bc89c10ecf4f1cd2c9f7588383c514503d19e0755c555e0e2d7c3c0286e9400000013259dd0a487127867aa3d623ee0f37a584cf01ab630353a7ac05fc91a8b42ac371168bd89f224a382265bf71ed2d160ba070bec86aa97430275c2f17d85b00a93466dc751bda61ec2b8bac99f5aaa9c106fc7fe2d0c8511d6166ef0f1125611535a4dabcb1fb7c3eac1c0fb69ee8e5da963c1ae742086570d0aec418a5f4e33c24b75a17c0ce5a8b82254dca612fb1960c4003a34c71524cdaf433b722c6f777e2f6318fca19040ee8177c2421302273ca383c2d3e5e6481dfc8ac36fca9fcc066c0d7c939f850eb4954ca54091f960a7fb241c3d6e310f82fb3e8aca821d84c80e8dad2e2bbca1f3af28546d9c6206bc92b5733334052e1f9d15a27e3d6e42bc0e6500639a452237c35de5fd17621529f3353582b0dd67176a97d3f6875497091e5969f8a611831201706e1841d178469b892011abb42ecb2d8cde663379af2b5c3b71be2df105f24b79d4f3496e9036078497a9c2cc730e1d42a46f94124430058af2892184600d680603c368b0756f56695949ce5ac31c88afdb35b0bcdcd76d2081b73ed943a0dd0a540eb58573e017eef9552c3283da789b1607e545653064124019c01c3ee2560ce1edb15426ebdf53b7ab01801fd4e5223140d1bc09171c85691d782ed979dcae51bac7c19271c4dfdcc762869cd3ba7c6f7e2c311c3565c900a94d252d08072022e5af5badd124c7d6943795760807886c0608fb19f201b560c57dff479acce03e8f9f8ef78a67959aee83d810c32d870aa4dac5c41a6a30a08e04c150bb8040c4a814475c0df7ab1519f5a430ae0d08e7bb6af24a95140f4dec329d526128176e4f8e04838a84eb99442f7d93dc8fce2a2ba0bec01c737c765dcefb32da1b66fae19c74967eab6ba9e1286253f88042f2707ba4832a4140b8bf8a9c19357b3a5c25f379106ced2f1d6cbb90542843c9db1dff77db2b1ed6833a6886e80a90adbea68bd2937154ac860bebf5cc4d875b9f347af7f2a000bc2bc5a58cb15083ea1931938142fedb15f0f6c6ebfcab2f49f08ada620e246f84e1f1ef2fe23ce3a20596e7c668840b60759c1168be66e9ce30f537ad97a83cc472f9164bcd8c114147c26b556aa15f32a4e2a5ff02449e4006f1ab8b4b7e49973c6ceaef4e73007befd6396b2bcf23a63e310b049836cc880b18354035b805b2b887155f94d6136f6b6f9e41505c29251e82a247ab8b5fd624a841eac95f3564f94041a3f9c0d199d247275e4273c4652aa1df9576a67fc11ee4f3016c70186c2019f4e1eaac2f0913e21d810a382e12dfc1a2eecd2d23b2c2605cd38c4c1364246ccdac898cb840fde79c85c60c2e83ba1a7d1d0312998f30cbb1b1f9a9248de1abe48c7f43451e3a7c63568ba7edfa5d45585694d660c90aad128bcfa150fd13853d817d3167110548664e9f66f862b80a96ad25e1a18d7040e0b2af7452439399d94180df45c8bc6d3b273b8d94718a9b059be3d9a069bc8c26553b895459694350e475861b3295e1e7797c1b311aa32f7c02e42b055a841b248cc71067f45a743c89e59ec7393e5a46f2b4342fa7d2542df4e83994cbe9a8ccf9118d35400bffcf8ed4398e55ac7b766967f3da05c739ff471e76e909bfcca2ad7103381f7ec03515cfa8f6fcb54eeec47d3673dc76f2c9d17dd17f01aa31a340949d24fdf4612eea2e60b7bf3fe364953c6dd5ff707f390a1b8a4ddbfc2de4888c31".to_string(), + proof_reg_id_hex: "2a0162a32a55d7e97071c4d89e286d2e725ca0fcce1f53f6c4e3d09bd1b5f0941f35ea82fab665a0f3efce90f4ca2f16ec8707e1bf1b7df84e764df3bfc60af83db2f0e1771e579c8b062be59eed03537ed1e52830c809068075c173bf6809f56443fe6dfa7a3e00ad097824b7c186fb72e771106a7c84a2005ba3e8272db1e11d97b5dbbc51cea52bc03a8fe5384af339be77a1cf838ccf7de0a065805f7cc4".to_string(), + signature_hex: "8b57451744c536ed188e3140143b5e43db5d44fc1a75b484ad45387b5b9d4c39d88018a5ef95edffedca34f36885a475b5bee57c0ac1bced114a4f9bacf75f98f93ae3468ea10c4198288b91a001150a007db6e1d2477a8b2d76bf64193b63ea".to_string(), + }, + revocation_threshold: 2, + }, + signatures_hex: HashMap::from([ + (0, "844f5747150117268c40c178ebeca13efc422e3894c14490b7b0a9528aece06c70eecea8bba223658491c550b15d0d649005622a0af4df0fab267fffe1604609".to_string()), + ]), + }; + let res = account_credential_deployment_signed_payload_hex(input).unwrap(); + assert_eq!( + res, + "010100002581eca64c0c480d4786c8878553f064a008b82be869af0e12149bdf26b62a5301a9e510d6685f81c2d7f8a0177222d067982cc73699c5a7d64a80d06543797e808e04c52ea393ee300f50d5b4c6e87c570000000002000300000001a52beef5e3c6491fce7012a268ee5b3ed03aeecdb496abd979bcc60803d6d3c970112599d500690c2f447daefb81223d99de5fd822fdfb7ce4c9fe7253995c639f9037ad64a529393ea5cc7db18b74432469208e0076a80ed2c22f7848b8289500000002994686187e9568e1076a6a711068dae4bd663f6ca2886fb3f97254ff0cc4e9b17797fd2a19237d8ca94941b697a3d7308b9a8e1a12f3eb856d52d3bb3adc016ba8c8eacbe93339a920f436de86f66ed847a1b03aab3645c1635d956b723442c8000000038af70ed13186d7c332bb894725c99ba9e58405a668cb3c9c48d6e85029284d29a91d8da59dacd0d85c925659426e198396c3f73e791ccec217bc92fbdc618877544b9a2789b4ea89d4cdcceb7df4cdde64284b400374585a59596543ae6c45d607e90207e802000000000dd18b57451744c536ed188e3140143b5e43db5d44fc1a75b484ad45387b5b9d4c39d88018a5ef95edffedca34f36885a475b5bee57c0ac1bced114a4f9bacf75f98f93ae3468ea10c4198288b91a001150a007db6e1d2477a8b2d76bf64193b63ea8067a3e8f17cba6c29fc66a697b69020e8bf3641ae35fa61d7a354c59f5ba83bf69aafd264f050a034c1c555dbbae047ab4c967385787ce6c1fb3e594b19152126255c06a53064ec39dec4f9763b2c06d2272b5358c1d4a6936d8d7f0910072d970b0b5512107183178a3b498351d8d7dc108a516fbe0dd783b45d566bd890bc0ed68e85c921bf6276c22dd1a81743bf000d00ae6563d450fe38afd17b0ab55274e8a3bdd02f01e1cc750166bce3f765d6225c22d32b5fd8b62b7f217f3d16fb94007b01ac5dfe414bd35411c6b9f01fe042f13c330e2a7bd756ba22c50df93b89e23b6c65802f7137144b0a4d47df6fb501e08002b2861ffea2f45ee63aabfb3d8aa2363b7bb58c80f4a5617dec6a6fa1cf5c23214a9566269d43bf8328b60db9426c470503981b672bf25fc45fb357b75ad0d17e104b226292521ed974d550ee39ba9bdae59af5b3b5794cf1598e2aa825755fa4bf04855e7d1ce5dda7b619c61026693f7c0e50eca18bfe4b100655e15eb6fa32f3413d971b5db5943e455f71e3196cd8d244058c494dc6eeade62ecc17436b9ea2a2b51e7bbcd0cf872d1a3986a3371b70b2e44a35d7c51a4cff7648ff68a7ef89f95e0680949b1f8d306ae0d44bb7e91c05a3e42ae40b68a70591abe06fa2e5acc769b489fa311e508a2eec7d015123181878d40788efcf20034fcdb5f14e1e41f357889b9fe7a1944769b3c2e84dfd11df406b7703d436317932b5e934a4ebb0bc59c6c508b751dd27e4d5413f183b8292b60da20cfd734f6942caad81c93a426d25951dcbab33254e0c86890f8aca0318197d938a09aca480cec324499b5fa291ca13712b20e841961f1abe1d4182f465bcbdcce77c3a52c409d41e2eb15819814d59d0be660aa8b7085c0f899c1bb1f44995869122433b910b5e8f90f63de69b9da6782bd72cc7a431b1d94d81d857bda28f649c194f0bad0cef63ef68a6998aadb1a59e11f15e853f6293a77d232d706d68b72d32ff4bb685cf91796447a10f06688f45ad3bba0ca0549009a431cbc581bb751d50e0e898658f68b5d25759022d8e5c05eaae18cad7e7d7746b8ea3b46459323cc02ee50b0000000000000002b7a9c414a294bf75a81826f5480a62d5fd7d1c83e385c3802e8889a47db1e63a5604e4aecfc5fda60127c60dd7bb1045916ae3e786c448ff873113f9f10d61cdffd1b85b3ad3bd89faf2e6c00b50d2ba0839e8a5f3816e171c89a7b942c0f1c4f418a4ae77726e04703cc5e7db08b69c649f8f76f903d4589fc50c2ddefb529c000000030000000158ee5cd5135a688e7bdccdead68a80bf4d7bee23017c2378ee725d39b5ad0e3b53a09a31179ad09783e0daa1ff71ddd98a3cc9f352f2ce09035a87dd47192f713ea09cdc83fe5b9c313c5da290bb063cd362787427c7bbdb828f869a44ccd347000000023aa278e933f551bd90e9edec2239278ae78b6d9afc76ac1244805331b7c569140d3134077a0c2d8ae6abed6b5caf89233988a8929cf759ca86e9a210811cae6670d40335cb3350d6511c308641ce3b4e40e75e406955f6b36fba2af9fb0bf89b000000035e15e30be5b8b5ec834c650300a32f7f13b8c2f68d4fcf072a2abaae9cebd2fb5f465c56cbcf675610e1a28789926e41973b40cca7ceba46c04b1dff5f581ec35ba32f9795d73e8ede135699394073e5a26b0ccbc006a4141f692cfc6f8ed6722a0bc89c10ecf4f1cd2c9f7588383c514503d19e0755c555e0e2d7c3c0286e9400000013259dd0a487127867aa3d623ee0f37a584cf01ab630353a7ac05fc91a8b42ac371168bd89f224a382265bf71ed2d160ba070bec86aa97430275c2f17d85b00a93466dc751bda61ec2b8bac99f5aaa9c106fc7fe2d0c8511d6166ef0f1125611535a4dabcb1fb7c3eac1c0fb69ee8e5da963c1ae742086570d0aec418a5f4e33c24b75a17c0ce5a8b82254dca612fb1960c4003a34c71524cdaf433b722c6f777e2f6318fca19040ee8177c2421302273ca383c2d3e5e6481dfc8ac36fca9fcc066c0d7c939f850eb4954ca54091f960a7fb241c3d6e310f82fb3e8aca821d84c80e8dad2e2bbca1f3af28546d9c6206bc92b5733334052e1f9d15a27e3d6e42bc0e6500639a452237c35de5fd17621529f3353582b0dd67176a97d3f6875497091e5969f8a611831201706e1841d178469b892011abb42ecb2d8cde663379af2b5c3b71be2df105f24b79d4f3496e9036078497a9c2cc730e1d42a46f94124430058af2892184600d680603c368b0756f56695949ce5ac31c88afdb35b0bcdcd76d2081b73ed943a0dd0a540eb58573e017eef9552c3283da789b1607e545653064124019c01c3ee2560ce1edb15426ebdf53b7ab01801fd4e5223140d1bc09171c85691d782ed979dcae51bac7c19271c4dfdcc762869cd3ba7c6f7e2c311c3565c900a94d252d08072022e5af5badd124c7d6943795760807886c0608fb19f201b560c57dff479acce03e8f9f8ef78a67959aee83d810c32d870aa4dac5c41a6a30a08e04c150bb8040c4a814475c0df7ab1519f5a430ae0d08e7bb6af24a95140f4dec329d526128176e4f8e04838a84eb99442f7d93dc8fce2a2ba0bec01c737c765dcefb32da1b66fae19c74967eab6ba9e1286253f88042f2707ba4832a4140b8bf8a9c19357b3a5c25f379106ced2f1d6cbb90542843c9db1dff77db2b1ed6833a6886e80a90adbea68bd2937154ac860bebf5cc4d875b9f347af7f2a000bc2bc5a58cb15083ea1931938142fedb15f0f6c6ebfcab2f49f08ada620e246f84e1f1ef2fe23ce3a20596e7c668840b60759c1168be66e9ce30f537ad97a83cc472f9164bcd8c114147c26b556aa15f32a4e2a5ff02449e4006f1ab8b4b7e49973c6ceaef4e73007befd6396b2bcf23a63e310b049836cc880b18354035b805b2b887155f94d6136f6b6f9e41505c29251e82a247ab8b5fd624a841eac95f3564f94041a3f9c0d199d247275e4273c4652aa1df9576a67fc11ee4f3016c70186c2019f4e1eaac2f0913e21d810a382e12dfc1a2eecd2d23b2c2605cd38c4c1364246ccdac898cb840fde79c85c60c2e83ba1a7d1d0312998f30cbb1b1f9a9248de1abe48c7f43451e3a7c63568ba7edfa5d45585694d660c90aad128bcfa150fd13853d817d3167110548664e9f66f862b80a96ad25e1a18d7040e0b2af7452439399d94180df45c8bc6d3b273b8d94718a9b059be3d9a069bc8c26553b895459694350e475861b3295e1e7797c1b311aa32f7c02e42b055a841b248cc71067f45a743c89e59ec7393e5a46f2b4342fa7d2542df4e83994cbe9a8ccf9118d35400bffcf8ed4398e55ac7b766967f3da05c739ff471e76e909bfcca2ad7103381f7ec03515cfa8f6fcb54eeec47d3673dc76f2c9d17dd17f01aa31a340949d24fdf4612eea2e60b7bf3fe364953c6dd5ff707f390a1b8a4ddbfc2de4888c312a0162a32a55d7e97071c4d89e286d2e725ca0fcce1f53f6c4e3d09bd1b5f0941f35ea82fab665a0f3efce90f4ca2f16ec8707e1bf1b7df84e764df3bfc60af83db2f0e1771e579c8b062be59eed03537ed1e52830c809068075c173bf6809f56443fe6dfa7a3e00ad097824b7c186fb72e771106a7c84a2005ba3e8272db1e11d97b5dbbc51cea52bc03a8fe5384af339be77a1cf838ccf7de0a065805f7cc40100844f5747150117268c40c178ebeca13efc422e3894c14490b7b0a9528aece06c70eecea8bba223658491c550b15d0d649005622a0af4df0fab267fffe1604609a795ea39e70e07b36a6378ba5e3584ea8623bcd78fceddff98384f8e0e851358fa830682a0b47df17cd26060e372bf0ca84c5e346b51308e602bbf8df6a20214c688ed12cc061360fc939bc91317197b4064be67245d590fa92f8010f55edf41b1429cc8a95a4fab01d5a4d9781a2c04c9fd963eb474b2746f908efd224c57caccc42626051fcc21f2ee46df1985e412878bdcdb35aa9eaf17e5f3413e079a87a28cd46afd1b2727d7fb65825c3101404c466d5a8cd853cbb73b79c852eb3aa3666fda7d9a555d7fca5e1d22477b695de1eb8ccd975847f233d512d170d1109c3bc6648d273473a4a535d3948df6850da5eaa4c807eb107e0a0dee867a9843a348efc5bba02f31daacc066951dbec22f956b3e423f6ab42d660a899bea9a64ee0000000490bf02717fc3f1015f005604496a8beadff179d2b0787ab18baba7ed6061f4d91deb511b6ef3f9bd6596a726bcc8af8e932bf7c21f810e038346a21474deda3f1ad7bdf559fea924c21301d2f95200b4a04791bdd31d5426bd40f51cbea8c6ff83818561c024c354001cc9b654e118544f8498a8c75bc82a3eece23d32ce18f43eec6edddec81e1261948a6d9d934bc7b7db029dcbf7cf4211b5cdd76848dc56567600c9107aff5a4e63783992708ecb205634c3c8964af4c7cf97d43f778c628e0d6f52e999028c854c2ff019768a7ac8a701c795f5c2cf2aa698a9c7e11bf1697e6f9763ce4b9138e5a2d2b2610cefb298266c19d0b86f937a08ddaf0f90d74ae0a63a5a0b0c7da69f764e1597407ffd0a7dfdfea0e75ffcb8fcfe23495f8cb4099429eb0c04a08823c18f65f060c631c69e7b535c75192e3d7bdca1c83a9408d76e2eb2dc872197bab615b588633a8da691dafa08d113d169d91ff3dbdcfb33bf4ead45c68963f7e79ec32da43bdda70f1a9f0f8154664cd355b3088616005dab4a86e3b1636b51c8cc1899d4764608cfe57128d96dbd860deef8c44ea9084c2b9e5f7da3bdf513743b9e86b75205c43d46fa244acc17c77b64a238cd4a9a", + ); +}