From 8a9145323dbbe3903f25ef6103326b26b1264a66 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Fri, 10 Nov 2023 17:01:41 +0000 Subject: [PATCH 01/17] Refactor Resolver methods into trait + free functions --- trustchain-api/src/api.rs | 2 +- trustchain-cli/src/bin/main.rs | 2 +- trustchain-core/src/chain.rs | 2 +- trustchain-core/src/resolver.rs | 510 +++++++++++++++---------------- trustchain-http/src/resolver.rs | 2 +- trustchain-ion/src/attest.rs | 1 + trustchain-ion/src/verifier.rs | 2 +- trustchain-ion/tests/resolver.rs | 1 + 8 files changed, 256 insertions(+), 266 deletions(-) diff --git a/trustchain-api/src/api.rs b/trustchain-api/src/api.rs index e03c27de..2609aece 100644 --- a/trustchain-api/src/api.rs +++ b/trustchain-api/src/api.rs @@ -14,7 +14,7 @@ use trustchain_core::{ chain::DIDChain, holder::Holder, issuer::{Issuer, IssuerError}, - resolver::{Resolver, ResolverResult}, + resolver::{Resolver, ResolverResult, TrustchainResolver}, vc::CredentialError, verifier::{Timestamp, Verifier, VerifierError}, vp::PresentationError, diff --git a/trustchain-cli/src/bin/main.rs b/trustchain-cli/src/bin/main.rs index b8ceb029..a08a3bdf 100644 --- a/trustchain-cli/src/bin/main.rs +++ b/trustchain-cli/src/bin/main.rs @@ -11,7 +11,7 @@ use trustchain_api::{ TrustchainAPI, }; use trustchain_cli::config::cli_config; -use trustchain_core::{vc::CredentialError, verifier::Verifier}; +use trustchain_core::{vc::CredentialError, verifier::Verifier, resolver::TrustchainResolver}; use trustchain_ion::{ attest::attest_operation, create::{create_operation, create_operation_mnemonic}, diff --git a/trustchain-core/src/chain.rs b/trustchain-core/src/chain.rs index 5a64e67e..6fe92d1b 100644 --- a/trustchain-core/src/chain.rs +++ b/trustchain-core/src/chain.rs @@ -1,6 +1,6 @@ //! Chain API and `DIDChain` type with default implementation. use crate::display::PrettyDID; -use crate::resolver::Resolver; +use crate::resolver::{Resolver, TrustchainResolver}; use crate::utils::{canonicalize, decode, decode_verify, extract_keys, hash}; use serde::{Deserialize, Serialize}; use ssi::did_resolve::Metadata; diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index a49f55ba..e7fa8067 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -9,8 +9,8 @@ use ssi::did_resolve::{ use ssi::one_or_many::OneOrMany; use std::collections::HashMap; use thiserror::Error; - use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; +use crate::utils::HasEndpoints; /// An error relating to Trustchain resolution. #[derive(Error, Debug)] @@ -57,50 +57,52 @@ pub type ResolverResult = Result< // See https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types. pub struct DIDMethodWrapper(pub S); +#[async_trait] impl DIDResolver for DIDMethodWrapper { - fn resolve<'life0, 'life1, 'life2, 'async_trait>( - &'life0 self, - did: &'life1 str, - input_metadata: &'life2 ResolutionInputMetadata, - ) -> core::pin::Pin< - Box< - dyn core::future::Future< - Output = ( - ResolutionMetadata, - Option, - Option, - ), - > + core::marker::Send - + 'async_trait, - >, - > - where - 'life0: 'async_trait, - 'life1: 'async_trait, - 'life2: 'async_trait, - Self: 'async_trait, - { - self.0.to_resolver().resolve(did, input_metadata) + async fn resolve(&self, did: &str, input_metadata: &ResolutionInputMetadata, + ) -> ( + ResolutionMetadata, + Option, + Option, + ){ + self.0.to_resolver().resolve(did, input_metadata).await } } -// DIDMethodWrapper is used only to upcast a DIDMethod to a DIDResolver, -// and resolvers do not change shared state, so we can guarantee safety -// on Sync & Send. Both are empty implementations. -unsafe impl Sync for DIDMethodWrapper {} -unsafe impl Send for DIDMethodWrapper {} +// pub trait CASClient { +// fn client() -> Option String> { +// None +// } +// } /// Struct for performing resolution from a sidetree server to generate /// Trustchain DID document and DID document metadata. -pub struct Resolver { - /// Resolver for performing DID Method resolutions. - wrapped_resolver: T, +pub struct Resolver { + pub wrapped_resolver: T } -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -impl DIDResolver for Resolver { - async fn resolve( +impl Resolver { + /// Constructs a Trustchain resolver. + pub fn new(resolver: T) -> Self { + Self { + wrapped_resolver: resolver, + } + } + /// Constructs a Trustchain resolver from a DIDMethod. + pub fn from(method: S) -> Resolver> { + // Wrap the DIDMethod. + Resolver::>::new(DIDMethodWrapper::(method)) + } +} + +impl TrustchainResolver for Resolver where T: DIDResolver + Sync + Send { + // use default impls on the trait def + // (grants .transform method) +} + +#[async_trait] +impl DIDResolver for Resolver where T: DIDResolver + Sync + Send{ +async fn resolve( &self, did: &str, input_metadata: &ResolutionInputMetadata, @@ -116,25 +118,190 @@ impl DIDResolver for Resolver { .resolve(did, &ResolutionInputMetadata::default()) .await; } + // let ion_resolver = self.wrapped_resolver; + // let resolved = ion_resolver.resolve(did, input_metadata).await; + + let resolved = self.wrapped_resolver.resolve(did, input_metadata).await; + // Consider using ResolutionInputMetadata to optionally not perform transform. // Resolve with the wrapped DIDResolver and then transform to Trustchain format. - self.transform(self.wrapped_resolver.resolve(did, input_metadata).await) + self.transform(resolved) } } -impl Resolver { - /// Constructs a Trustchain resolver. - pub fn new(resolver: T) -> Self { - Self { - wrapped_resolver: resolver, + +/// Adds the controller property to a resolved DID document, using the +/// value passed in the controller_did argument. This must be the DID of +/// the subject (id property) found in the upstream DID document. +fn add_controller( + mut doc: Document, + controller_did: &str, +) -> Result { + // Check controller is empty and if not throw error. + if doc.controller.is_some() { + return Err(ResolverError::ControllerAlreadyPresent); + } + + // Add the controller property to the DID document. + doc.controller = Some(OneOrMany::One(controller_did.to_string())); + + // Return updated DID document. + Ok(doc) +} + +/// Gets a result of an index of a single Trustchain proof service, otherwise relevant error. +fn get_proof_idx(doc: &Document) -> Result { + let mut idxs: Vec = Vec::new(); + let fragment = TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; + for (idx, service) in doc.service.iter().flatten().enumerate() { + if let [service_fragment, _] = + service.id.rsplitn(2, '#').collect::>().as_slice() + { + if service_fragment == &fragment { + idxs.push(idx); + } + } + } + match idxs.len() { + 0 => Err(ResolverError::NoTrustchainProofService), + 1 => Ok(idxs[0]), + _ => Err(ResolverError::MultipleTrustchainProofService), + } +} + +/// Gets a result of a reference to a single Trustchain proof service, otherwise relevant error. +fn get_proof_service(doc: &Document) -> Result<&Service, ResolverError> { + // Extract proof service as an owned service + let idxs = get_proof_idx(doc); + match idxs { + Ok(idx) => Ok(&doc.service.as_ref().unwrap()[idx]), + Err(e) => Err(e), + } +} + +/// Gets the value of a key in a Trustchain proof service. +fn get_from_proof_service<'a>( + proof_service: &'a Service, + key: &str, +) -> Option<&'a String> { + // Destructure nested enums and extract controller from a proof service + let value: Option<&String> = match proof_service.service_endpoint.as_ref() { + Some(OneOrMany::One(ServiceEndpoint::Map(Value::Object(v)))) => match &v[key] { + Value::String(s) => Some(s), + _ => None, + }, + _ => None, + }; + value +} + +/// Adds a proof from a DID Document to DocumentMetadata. +fn add_proof(doc: &Document, mut doc_meta: DocumentMetadata) -> DocumentMetadata { + // Check if the Trustchain proof service exists in document + + // Get proof service + let proof_service = get_proof_service(doc); + + // Handle result + if let Ok(proof_service) = proof_service { + // Get proof value and controller (uDID) + let proof_value = get_from_proof_service(proof_service, "proofValue"); + let controller = get_from_proof_service(proof_service, "controller"); + // If not None, add to new HashMap + if let (Some(property_set), Some(proof_value), Some(controller)) = + (doc_meta.property_set.as_mut(), proof_value, controller) + { + // Make new HashMap; add keys and values + let mut proof_hash_map: HashMap = HashMap::new(); + proof_hash_map.insert(String::from("id"), Metadata::String(controller.to_owned())); + proof_hash_map.insert( + String::from("type"), + Metadata::String("JsonWebSignature2020".to_string()), + ); + proof_hash_map.insert( + String::from("proofValue"), + Metadata::String(proof_value.to_owned()), + ); + + // Insert new HashMap of Metadata::Map() + property_set.insert(String::from("proof"), Metadata::Map(proof_hash_map)); + return doc_meta; } } + // If there are zero or multiple proof services, do nothing + doc_meta +} - /// Constructs a Trustchain resolver from a DIDMethod. - pub fn from(method: S) -> Resolver> { - // Wrap the DIDMethod. - Resolver::>::new(DIDMethodWrapper::(method)) +/// Removes Trustchain proof service from passed document if it exists. +fn remove_proof_service(mut doc: Document) -> Document { + if doc.service.is_some() { + let idx_result = get_proof_idx(&doc); + if let Ok(idx) = idx_result { + let services = doc.service.as_mut().unwrap(); + services.remove(idx); + if services.is_empty() { + doc.service = None; + } + } } + doc +} + +/// Converts a DID Document from a resolved DID to the Trustchain resolved format. +fn transform_doc(doc: &Document, controller_did: &str) -> Document { + // Clone the passed DID document. + let doc_clone = doc.clone(); + + // // TODO: CAS keys + // // Add any keys from IPFS + // let doc_clone = self.add_cas_keys(doc_clone); + + // Duplication? + // // Check if the Trustchain proof service alreday exists in the document. + // let doc_clone = self.remove_proof_service(doc_clone); + + // Add controller + let doc_clone = add_controller(doc_clone, controller_did) + .expect("Controller already present in document."); + + // Remove the proof service from the document. + remove_proof_service(doc_clone) +} + +/// Converts DID Document Metadata from a resolved DID to the Trustchain resolved format. +fn transform_doc_metadata( + doc: &Document, + doc_meta: DocumentMetadata, +) -> DocumentMetadata { + // Add proof property to the DID Document Metadata (if it exists). + add_proof(doc, doc_meta) +} + +//___________________ + +/// Trait for performing Trustchain resolution. +#[async_trait] +pub trait TrustchainResolver : DIDResolver { + // async fn resolve( + // &self, + // did: &str, + // input_metadata: &ResolutionInputMetadata, + // ) -> ( + // ResolutionMetadata, + // Option, + // Option, + // ) { + // // TODO: remove upon handling with DIDMethods + // if did.starts_with("did:key:") { + // let did_key_resolver = DIDKey; + // return did_key_resolver + // .resolve(did, &ResolutionInputMetadata::default()) + // .await; + // } + // // Consider using ResolutionInputMetadata to optionally not perform transform. + // // Resolve with the wrapped DIDResolver and then transform to Trustchain format. + // self.transform(self.resolve(did, input_metadata).await) + // } /// Transforms the result of a DID resolution into the Trustchain format. fn transform( @@ -190,9 +357,9 @@ impl Resolver { } } - /// Sync Trustchain resolve function returning resolution metadata, + /// Sync Trustchain resolve function returning resolution metadata, /// DID document and DID document metadata from a passed DID as a `Result` type. - pub async fn resolve_as_result(&self, did: &str) -> ResolverResult { + async fn resolve_as_result(&self, did: &str) -> ResolverResult { // sidetree resolved resolution metadata, document and document metadata let (did_res_meta, did_doc, did_doc_meta) = self.resolve(did, &ResolutionInputMetadata::default()).await; @@ -228,162 +395,15 @@ impl Resolver { } } - /// Gets a result of an index of a single Trustchain proof service, otherwise relevant error. - fn get_proof_idx(&self, doc: &Document) -> Result { - let mut idxs: Vec = Vec::new(); - let fragment = TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; - for (idx, service) in doc.service.iter().flatten().enumerate() { - if let [service_fragment, _] = - service.id.rsplitn(2, '#').collect::>().as_slice() - { - if service_fragment == &fragment { - idxs.push(idx); - } - } - } - match idxs.len() { - 0 => Err(ResolverError::NoTrustchainProofService), - 1 => Ok(idxs[0]), - _ => Err(ResolverError::MultipleTrustchainProofService), - } - } - - /// Gets a result of a reference to a single Trustchain proof service, otherwise relevant error. - fn get_proof_service<'a>(&'a self, doc: &'a Document) -> Result<&Service, ResolverError> { - // Extract proof service as an owned service - let idxs = self.get_proof_idx(doc); - match idxs { - Ok(idx) => Ok(&doc.service.as_ref().unwrap()[idx]), - Err(e) => Err(e), - } - } - - /// Removes Trustchain proof service from passed document if it exists. - fn remove_proof_service(&self, mut doc: Document) -> Document { - if doc.service.is_some() { - let idx_result = self.get_proof_idx(&doc); - if let Ok(idx) = idx_result { - let services = doc.service.as_mut().unwrap(); - services.remove(idx); - if services.is_empty() { - doc.service = None; - } - } - } - doc - } - - /// Gets the value of a key in a Trustchain proof service. - fn get_from_proof_service<'a>( - &self, - proof_service: &'a Service, - key: &str, - ) -> Option<&'a String> { - // Destructure nested enums and extract controller from a proof service - let value: Option<&String> = match proof_service.service_endpoint.as_ref() { - Some(OneOrMany::One(ServiceEndpoint::Map(Value::Object(v)))) => match &v[key] { - Value::String(s) => Some(s), - _ => None, - }, - _ => None, - }; - value - } - - /// Adds a proof from a DID Document to DocumentMetadata. - fn add_proof(&self, doc: &Document, mut doc_meta: DocumentMetadata) -> DocumentMetadata { - // Check if the Trustchain proof service exists in document - - // Get proof service - let proof_service = self.get_proof_service(doc); - - // Handle result - if let Ok(proof_service) = proof_service { - // Get proof value and controller (uDID) - let proof_value = self.get_from_proof_service(proof_service, "proofValue"); - let controller = self.get_from_proof_service(proof_service, "controller"); - // If not None, add to new HashMap - if let (Some(property_set), Some(proof_value), Some(controller)) = - (doc_meta.property_set.as_mut(), proof_value, controller) - { - // Make new HashMap; add keys and values - let mut proof_hash_map: HashMap = HashMap::new(); - proof_hash_map.insert(String::from("id"), Metadata::String(controller.to_owned())); - proof_hash_map.insert( - String::from("type"), - Metadata::String("JsonWebSignature2020".to_string()), - ); - proof_hash_map.insert( - String::from("proofValue"), - Metadata::String(proof_value.to_owned()), - ); - - // Insert new HashMap of Metadata::Map() - property_set.insert(String::from("proof"), Metadata::Map(proof_hash_map)); - return doc_meta; - } - } - // If there are zero or multiple proof services, do nothing - doc_meta - } - - /// Adds the controller property to a resolved DID document, using the - /// value passed in the controller_did argument. This must be the DID of - /// the subject (id property) found in the upstream DID document. - fn add_controller( - &self, - mut doc: Document, - controller_did: &str, - ) -> Result { - // Check controller is empty and if not throw error. - if doc.controller.is_some() { - return Err(ResolverError::ControllerAlreadyPresent); - } - - // Add the controller property to the DID document. - doc.controller = Some(OneOrMany::One(controller_did.to_string())); - - // Return updated DID document. - Ok(doc) - } - - /// Converts DID Document Metadata from a resolved DID to the Trustchain resolved format. - pub fn transform_doc_metadata( - &self, - doc: &Document, - doc_meta: DocumentMetadata, - ) -> DocumentMetadata { - // Add proof property to the DID Document Metadata (if it exists). - self.add_proof(doc, doc_meta) - } - - /// Converts a DID Document from a resolved DID to the Trustchain resolved format. - pub fn transform_doc(&self, doc: &Document, controller_did: &str) -> Document { - // Clone the passed DID document. - let doc_clone = doc.clone(); - - // Duplication? - // // Check if the Trustchain proof service alreday exists in the document. - // let doc_clone = self.remove_proof_service(doc_clone); - - // Add controller - let doc_clone = self - .add_controller(doc_clone, controller_did) - .expect("Controller already present in document."); - - // Remove the proof service from the document. - self.remove_proof_service(doc_clone) - } - /// Converts DID Document + Metadata to the Trustchain resolved format. - pub fn transform_as_result( + fn transform_as_result( &self, sidetree_res_meta: ResolutionMetadata, sidetree_doc: Document, sidetree_doc_meta: DocumentMetadata, ) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { // Get controller DID - let service = self.get_proof_service(&sidetree_doc); + let service = get_proof_service(&sidetree_doc); // Return immediately multiple proof services present if let Err(ResolverError::MultipleTrustchainProofService) = service { @@ -391,13 +411,13 @@ impl Resolver { }; if let Ok(service) = service { - let controller_did = self.get_from_proof_service(service, "controller"); + let controller_did = get_from_proof_service(service, "controller"); // Convert doc - let doc = self.transform_doc(&sidetree_doc, controller_did.unwrap().as_str()); + let doc = transform_doc(&sidetree_doc, controller_did.unwrap().as_str()); // Convert metadata - let doc_meta = self.transform_doc_metadata(&sidetree_doc, sidetree_doc_meta); + let doc_meta = transform_doc_metadata(&sidetree_doc, sidetree_doc_meta); // Convert resolution metadata let res_meta = sidetree_res_meta; @@ -430,7 +450,7 @@ mod tests { } #[test] - fn add_controller() { + fn test_add_controller() { // Test add_controller method with successful result. let controller_did = "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9YP"; @@ -446,8 +466,7 @@ mod tests { let resolver = Resolver::new(get_http_resolver()); // Call add_controller on the Resolver to get the result. - let result = resolver - .add_controller(did_doc, controller_did) + let result = add_controller(did_doc, controller_did) .expect("Different Controller already present."); // Check there *is* a controller field in the resulting DID document. @@ -467,7 +486,7 @@ mod tests { } #[test] - fn add_controller_fail() { + fn test_add_controller_fail() { // Test add_controller method with failure as controller already present. let controller_did = "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9YP"; @@ -483,7 +502,7 @@ mod tests { let resolver = Resolver::new(get_http_resolver()); // Attempt to add the controller. - let result = resolver.add_controller(did_doc, controller_did); + let result = add_controller(did_doc, controller_did); // Confirm error. assert!(matches!( @@ -493,7 +512,7 @@ mod tests { } #[test] - fn remove_proof_service() { + fn test_remove_proof_service() { // Test remove_proof_service method with successful result. // Load a Sidetree-resolved DID Document. @@ -503,18 +522,15 @@ mod tests { // Check the proof service is present. assert!(did_doc.service.is_some()); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Remove the proof service in the DID document. - let did_doc_no_proof_service = resolver.remove_proof_service(did_doc); + let did_doc_no_proof_service = remove_proof_service(did_doc); // Check the proof service has been removed. assert!(did_doc_no_proof_service.service.is_none()); } #[test] - fn get_proof_service() { + fn test_get_proof_service() { // Test get_proof_service method on a sidetree-resolved DID document. // Load a Sidetree-resolved DID Document. @@ -524,11 +540,8 @@ mod tests { // Check that precisely one service is present in the DID document. assert_eq!(did_doc.service.as_ref().unwrap().len(), 1_usize); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Get the service property containing the Trustchain proof. - let proof_service = resolver.get_proof_service(&did_doc).unwrap(); + let proof_service = get_proof_service(&did_doc).unwrap(); // Check the contents of the proof service property. assert_eq!(proof_service.id, format!("#trustchain-controller-proof")); @@ -539,7 +552,7 @@ mod tests { } #[test] - fn get_proof_service_only() { + fn test_get_proof_service_only() { // Test get_proof_service method when non-proof service is present. // Load a Sidetree-resolved DID Document. @@ -549,11 +562,8 @@ mod tests { // Check that two services are present in the DID document. assert_eq!(did_doc.service.as_ref().unwrap().len(), 2_usize); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Get the service property containing the Trustchain proof. - let proof_service = resolver.get_proof_service(&did_doc).unwrap(); + let proof_service = get_proof_service(&did_doc).unwrap(); // Check the contents of the proof service property. assert_eq!(proof_service.id, format!("#trustchain-controller-proof")); @@ -564,7 +574,7 @@ mod tests { } #[test] - fn get_proof_service_fail_multiple_proof_services() { + fn test_get_proof_service_fail_multiple_proof_services() { // Test get_proof_service method with failure as multiple proof services present. // Construct a DID Document with muliple proof services. @@ -574,10 +584,7 @@ mod tests { // Check that two services are present in the DID document. assert_eq!(did_doc.service.as_ref().unwrap().len(), 2_usize); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - - let result = resolver.get_proof_service(&did_doc); + let result = get_proof_service(&did_doc); // Expect an error due to the presence of multiple proof services. assert!(matches!( @@ -587,7 +594,7 @@ mod tests { } #[test] - fn get_proof_service_fail_no_proof_services() { + fn test_get_proof_service_fail_no_proof_services() { // Test get_proof_service method with failure as no proof services present. // Construct a DID Document with a service but no proof services. @@ -597,10 +604,7 @@ mod tests { // Check that a service is present in the DID document. assert!(did_doc.service.is_some()); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - - let result = resolver.get_proof_service(&did_doc); + let result = get_proof_service(&did_doc); // // Expect an error due to the absence of any proof services. assert!(matches!( @@ -610,7 +614,7 @@ mod tests { } #[test] - fn get_proof_service_fail_no_services() { + fn test_get_proof_service_fail_no_services() { // Test get_proof_service method with failure as no services present. // Construct a DID Document with no proof services. @@ -620,10 +624,7 @@ mod tests { // Check that no services are present in the DID document. assert!(did_doc.service.is_none()); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - - let result = resolver.get_proof_service(&did_doc); + let result = get_proof_service(&did_doc); // Expect an error due to the absence of any proof services. assert!(matches!( @@ -633,22 +634,18 @@ mod tests { } #[test] - fn get_from_proof_service() { + fn test_get_from_proof_service() { // Test to extract the controller DID from the service field in a sidetree-resolved DID document. // Load a Sidetree-resolved DID Document. let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load."); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Get a reference to the proof service. - let service = resolver.get_proof_service(&did_doc).unwrap(); + let service = get_proof_service(&did_doc).unwrap(); // Get the controller DID from the proof service. - let controller = resolver - .get_from_proof_service(service, "controller") + let controller = get_from_proof_service(service, "controller") .unwrap(); // Check the controller DID matches the expected value. @@ -659,7 +656,7 @@ mod tests { } #[test] - fn add_proof() { + fn test_add_proof() { // Test adding a proof to DID Document Metadata. // Load a Sidetree-resolved DID Document. @@ -677,11 +674,8 @@ mod tests { let expected_tc_meta = canonicalize(&expected_tc_meta).expect("Cannot add proof and canonicalize."); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Add proof to the DID Document Metadata and canonicalize the result. - let actual_tc_meta = canonicalize(&resolver.add_proof(&sidetree_doc, sidetree_meta)) + let actual_tc_meta = canonicalize(&add_proof(&sidetree_doc, sidetree_meta)) .expect("Cannot add proof and canonicalize."); // Check that the result matches the expected metadata. @@ -689,7 +683,7 @@ mod tests { } #[test] - fn transform_doc_metadata() { + fn test_transform_doc_metadata() { // Test transformation of Sidetree-resolved DID Document Metadata to Trustchain format. // See https://github.com/alan-turing-institute/trustchain/issues/11 @@ -702,11 +696,8 @@ mod tests { let sidetree_meta: DocumentMetadata = serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA).expect("Failed to load metadata"); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Transform the DID Document Metadata by resolving into Trustchain format. - let actual = resolver.transform_doc_metadata(&did_doc, sidetree_meta); + let actual = transform_doc_metadata(&did_doc, sidetree_meta); // Canonicalise the result and compare with the expected Trustchain format. let canon_actual_meta = canonicalize(&actual).expect("Cannot add proof and canonicalize."); @@ -719,24 +710,21 @@ mod tests { } #[test] - fn transform_doc() { + fn test_transform_doc() { // Test transformation of a Sidetree-resolved DID Document into Trustchain format. // Load a Sidetree-resolved DID Document. let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load."); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Get the controller from the proof service property in the Sidetree-resolved DID document. - let proof_service = resolver.get_proof_service(&did_doc).unwrap(); - let controller = resolver - .get_from_proof_service(proof_service, "controller") + let proof_service = get_proof_service(&did_doc).unwrap(); + let controller = + get_from_proof_service(proof_service, "controller") .unwrap(); // Transform the DID document by resolving into Trustchain format. - let actual = resolver.transform_doc(&did_doc, controller.as_str()); + let actual = transform_doc(&did_doc, controller.as_str()); // Canonicalise the result and compare with the expected Trustchain format. let canon_actual_doc = canonicalize(&actual).expect("Failed to canonicalize."); @@ -749,7 +737,7 @@ mod tests { } #[test] - fn transform_as_result() { + fn test_transform_as_result() { // Test transformation of Sidetree-resolved DID Document + Metadata into Trustchain format. // Construct sample DID documents & metadata from test fixtures. diff --git a/trustchain-http/src/resolver.rs b/trustchain-http/src/resolver.rs index 1bf70d5a..b247b274 100644 --- a/trustchain-http/src/resolver.rs +++ b/trustchain-http/src/resolver.rs @@ -14,7 +14,7 @@ use ssi::{ }; use std::sync::Arc; use trustchain_core::chain::{Chain, DIDChain}; -use trustchain_core::resolver::Resolver; +use trustchain_core::resolver::{Resolver, TrustchainResolver}; use trustchain_core::verifier::{Timestamp, Verifier, VerifierError}; use trustchain_ion::verifier::{IONVerifier, VerificationBundle}; diff --git a/trustchain-ion/src/attest.rs b/trustchain-ion/src/attest.rs index 1481856c..1d9e312a 100644 --- a/trustchain-ion/src/attest.rs +++ b/trustchain-ion/src/attest.rs @@ -7,6 +7,7 @@ use serde_json::to_string_pretty as to_json; use std::convert::TryFrom; use trustchain_core::controller::Controller; use trustchain_core::key_manager::{ControllerKeyManager, KeyType}; +use trustchain_core::resolver::TrustchainResolver; use trustchain_core::subject::Subject; use trustchain_core::utils::get_operations_path; use trustchain_core::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; diff --git a/trustchain-ion/src/verifier.rs b/trustchain-ion/src/verifier.rs index 05aa0e27..b8414aa3 100644 --- a/trustchain-ion/src/verifier.rs +++ b/trustchain-ion/src/verifier.rs @@ -27,7 +27,7 @@ use std::sync::{Arc, Mutex}; use trustchain_core::commitment::{ CommitmentChain, CommitmentError, DIDCommitment, TimestampCommitment, }; -use trustchain_core::resolver::{Resolver, ResolverError}; +use trustchain_core::resolver::{Resolver, ResolverError, TrustchainResolver}; use trustchain_core::verifier::{Timestamp, VerifiableTimestamp, Verifier, VerifierError}; diff --git a/trustchain-ion/tests/resolver.rs b/trustchain-ion/tests/resolver.rs index 6314baeb..74e94ec6 100644 --- a/trustchain-ion/tests/resolver.rs +++ b/trustchain-ion/tests/resolver.rs @@ -2,6 +2,7 @@ use core::panic; use ssi::did_resolve::Metadata; use ssi::one_or_many::OneOrMany; +use trustchain_core::resolver::TrustchainResolver; use trustchain_ion::get_ion_resolver; #[tokio::test] From 62754ca5326bceb4be3534c448cb64765521bea7 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Fri, 10 Nov 2023 17:24:57 +0000 Subject: [PATCH 02/17] Initial move of resolver to trustchain-ion --- trustchain-cli/src/bin/main.rs | 2 +- trustchain-core/src/chain.rs | 4 +- trustchain-core/src/resolver.rs | 200 ++++++++------------------------ trustchain-core/src/verifier.rs | 7 +- trustchain-ion/src/lib.rs | 1 + trustchain-ion/src/resolver.rs | 100 ++++++++++++++++ 6 files changed, 159 insertions(+), 155 deletions(-) create mode 100644 trustchain-ion/src/resolver.rs diff --git a/trustchain-cli/src/bin/main.rs b/trustchain-cli/src/bin/main.rs index a08a3bdf..8d229c11 100644 --- a/trustchain-cli/src/bin/main.rs +++ b/trustchain-cli/src/bin/main.rs @@ -11,7 +11,7 @@ use trustchain_api::{ TrustchainAPI, }; use trustchain_cli::config::cli_config; -use trustchain_core::{vc::CredentialError, verifier::Verifier, resolver::TrustchainResolver}; +use trustchain_core::{resolver::TrustchainResolver, vc::CredentialError, verifier::Verifier}; use trustchain_ion::{ attest::attest_operation, create::{create_operation, create_operation_mnemonic}, diff --git a/trustchain-core/src/chain.rs b/trustchain-core/src/chain.rs index 6fe92d1b..01f7671c 100644 --- a/trustchain-core/src/chain.rs +++ b/trustchain-core/src/chain.rs @@ -1,6 +1,6 @@ //! Chain API and `DIDChain` type with default implementation. use crate::display::PrettyDID; -use crate::resolver::{Resolver, TrustchainResolver}; +use crate::resolver::TrustchainResolver; use crate::utils::{canonicalize, decode, decode_verify, extract_keys, hash}; use serde::{Deserialize, Serialize}; use ssi::did_resolve::Metadata; @@ -127,7 +127,7 @@ impl DIDChain { // Public constructor. pub async fn new( did: &str, - resolver: &Resolver, + resolver: &dyn TrustchainResolver, ) -> Result { // Construct an empty chain. let mut chain = DIDChain::empty(); diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index e7fa8067..dde719ac 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -1,4 +1,6 @@ //! DID resolution and `DIDResolver` implementation. +use crate::utils::HasEndpoints; +use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; use async_trait::async_trait; use did_method_key::DIDKey; use serde_json::Value; @@ -9,8 +11,6 @@ use ssi::did_resolve::{ use ssi::one_or_many::OneOrMany; use std::collections::HashMap; use thiserror::Error; -use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; -use crate::utils::HasEndpoints; /// An error relating to Trustchain resolution. #[derive(Error, Debug)] @@ -51,92 +51,16 @@ pub type ResolverResult = Result< ResolverError, >; -// Newtype pattern (workaround for lack of trait upcasting coercion). -// Specifically, the DIDMethod method to_resolver() returns a reference but we want ownership. -// The workaround is to define a wrapper for DIDMethod that implements DIDResolver. -// See https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types. -pub struct DIDMethodWrapper(pub S); - -#[async_trait] -impl DIDResolver for DIDMethodWrapper { - async fn resolve(&self, did: &str, input_metadata: &ResolutionInputMetadata, - ) -> ( - ResolutionMetadata, - Option, - Option, - ){ - self.0.to_resolver().resolve(did, input_metadata).await - } -} - // pub trait CASClient { // fn client() -> Option String> { // None // } // } -/// Struct for performing resolution from a sidetree server to generate -/// Trustchain DID document and DID document metadata. -pub struct Resolver { - pub wrapped_resolver: T -} - -impl Resolver { - /// Constructs a Trustchain resolver. - pub fn new(resolver: T) -> Self { - Self { - wrapped_resolver: resolver, - } - } - /// Constructs a Trustchain resolver from a DIDMethod. - pub fn from(method: S) -> Resolver> { - // Wrap the DIDMethod. - Resolver::>::new(DIDMethodWrapper::(method)) - } -} - -impl TrustchainResolver for Resolver where T: DIDResolver + Sync + Send { - // use default impls on the trait def - // (grants .transform method) -} - -#[async_trait] -impl DIDResolver for Resolver where T: DIDResolver + Sync + Send{ -async fn resolve( - &self, - did: &str, - input_metadata: &ResolutionInputMetadata, - ) -> ( - ResolutionMetadata, - Option, - Option, - ) { - // TODO: remove upon handling with DIDMethods - if did.starts_with("did:key:") { - let did_key_resolver = DIDKey; - return did_key_resolver - .resolve(did, &ResolutionInputMetadata::default()) - .await; - } - // let ion_resolver = self.wrapped_resolver; - // let resolved = ion_resolver.resolve(did, input_metadata).await; - - let resolved = self.wrapped_resolver.resolve(did, input_metadata).await; - - // Consider using ResolutionInputMetadata to optionally not perform transform. - // Resolve with the wrapped DIDResolver and then transform to Trustchain format. - self.transform(resolved) - } -} - - /// Adds the controller property to a resolved DID document, using the /// value passed in the controller_did argument. This must be the DID of /// the subject (id property) found in the upstream DID document. -fn add_controller( - mut doc: Document, - controller_did: &str, -) -> Result { +fn add_controller(mut doc: Document, controller_did: &str) -> Result { // Check controller is empty and if not throw error. if doc.controller.is_some() { return Err(ResolverError::ControllerAlreadyPresent); @@ -154,8 +78,7 @@ fn get_proof_idx(doc: &Document) -> Result { let mut idxs: Vec = Vec::new(); let fragment = TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; for (idx, service) in doc.service.iter().flatten().enumerate() { - if let [service_fragment, _] = - service.id.rsplitn(2, '#').collect::>().as_slice() + if let [service_fragment, _] = service.id.rsplitn(2, '#').collect::>().as_slice() { if service_fragment == &fragment { idxs.push(idx); @@ -180,10 +103,7 @@ fn get_proof_service(doc: &Document) -> Result<&Service, ResolverError> { } /// Gets the value of a key in a Trustchain proof service. -fn get_from_proof_service<'a>( - proof_service: &'a Service, - key: &str, -) -> Option<&'a String> { +fn get_from_proof_service<'a>(proof_service: &'a Service, key: &str) -> Option<&'a String> { // Destructure nested enums and extract controller from a proof service let value: Option<&String> = match proof_service.service_endpoint.as_ref() { Some(OneOrMany::One(ServiceEndpoint::Map(Value::Object(v)))) => match &v[key] { @@ -261,27 +181,56 @@ fn transform_doc(doc: &Document, controller_did: &str) -> Document { // let doc_clone = self.remove_proof_service(doc_clone); // Add controller - let doc_clone = add_controller(doc_clone, controller_did) - .expect("Controller already present in document."); + let doc_clone = + add_controller(doc_clone, controller_did).expect("Controller already present in document."); // Remove the proof service from the document. remove_proof_service(doc_clone) } /// Converts DID Document Metadata from a resolved DID to the Trustchain resolved format. -fn transform_doc_metadata( - doc: &Document, - doc_meta: DocumentMetadata, -) -> DocumentMetadata { +fn transform_doc_metadata(doc: &Document, doc_meta: DocumentMetadata) -> DocumentMetadata { // Add proof property to the DID Document Metadata (if it exists). add_proof(doc, doc_meta) } -//___________________ +/// Converts DID Document + Metadata to the Trustchain resolved format. +fn transform_as_result( + sidetree_res_meta: ResolutionMetadata, + sidetree_doc: Document, + sidetree_doc_meta: DocumentMetadata, +) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { + // Get controller DID + let service = get_proof_service(&sidetree_doc); + + // Return immediately multiple proof services present + if let Err(ResolverError::MultipleTrustchainProofService) = service { + return Err(ResolverError::MultipleTrustchainProofService); + }; + + if let Ok(service) = service { + let controller_did = get_from_proof_service(service, "controller"); + + // Convert doc + let doc = transform_doc(&sidetree_doc, controller_did.unwrap().as_str()); + + // Convert metadata + let doc_meta = transform_doc_metadata(&sidetree_doc, sidetree_doc_meta); + + // Convert resolution metadata + let res_meta = sidetree_res_meta; + + // Return tuple + Ok((res_meta, doc, doc_meta)) + } else { + // TODO: If proof service is not present or multiple, just return Ok for now. + Ok((sidetree_res_meta, sidetree_doc, sidetree_doc_meta)) + } +} /// Trait for performing Trustchain resolution. #[async_trait] -pub trait TrustchainResolver : DIDResolver { +pub trait TrustchainResolver: DIDResolver { // async fn resolve( // &self, // did: &str, @@ -320,7 +269,7 @@ pub trait TrustchainResolver : DIDResolver { // If a document and document metadata are returned, try to convert if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) { // Convert to trustchain versions - let tc_result = self.transform_as_result(res_meta, did_doc, did_doc_meta); + let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta); match tc_result { // Map the tuple of non-option types to have tuple with optional document // document metadata @@ -357,7 +306,7 @@ pub trait TrustchainResolver : DIDResolver { } } - /// Sync Trustchain resolve function returning resolution metadata, + /// Sync Trustchain resolve function returning resolution metadata, /// DID document and DID document metadata from a passed DID as a `Result` type. async fn resolve_as_result(&self, did: &str) -> ResolverResult { // sidetree resolved resolution metadata, document and document metadata @@ -394,41 +343,6 @@ pub trait TrustchainResolver : DIDResolver { Ok((did_res_meta, did_doc, did_doc_meta)) } } - - /// Converts DID Document + Metadata to the Trustchain resolved format. - fn transform_as_result( - &self, - sidetree_res_meta: ResolutionMetadata, - sidetree_doc: Document, - sidetree_doc_meta: DocumentMetadata, - ) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { - // Get controller DID - let service = get_proof_service(&sidetree_doc); - - // Return immediately multiple proof services present - if let Err(ResolverError::MultipleTrustchainProofService) = service { - return Err(ResolverError::MultipleTrustchainProofService); - }; - - if let Ok(service) = service { - let controller_did = get_from_proof_service(service, "controller"); - - // Convert doc - let doc = transform_doc(&sidetree_doc, controller_did.unwrap().as_str()); - - // Convert metadata - let doc_meta = transform_doc_metadata(&sidetree_doc, sidetree_doc_meta); - - // Convert resolution metadata - let res_meta = sidetree_res_meta; - - // Return tuple - Ok((res_meta, doc, doc_meta)) - } else { - // TODO: If proof service is not present or multiple, just return Ok for now. - Ok((sidetree_res_meta, sidetree_doc, sidetree_doc_meta)) - } - } } #[cfg(test)] @@ -462,12 +376,9 @@ mod tests { // Check there is no controller in the DID document. assert!(did_doc.controller.is_none()); - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Call add_controller on the Resolver to get the result. - let result = add_controller(did_doc, controller_did) - .expect("Different Controller already present."); + let result = + add_controller(did_doc, controller_did).expect("Different Controller already present."); // Check there *is* a controller field in the resulting DID document. assert!(result.controller.is_some()); @@ -499,7 +410,7 @@ mod tests { assert!(did_doc.controller.is_some()); // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); + // let resolver = Resolver::new(get_http_resolver()); // Attempt to add the controller. let result = add_controller(did_doc, controller_did); @@ -645,8 +556,7 @@ mod tests { let service = get_proof_service(&did_doc).unwrap(); // Get the controller DID from the proof service. - let controller = get_from_proof_service(service, "controller") - .unwrap(); + let controller = get_from_proof_service(service, "controller").unwrap(); // Check the controller DID matches the expected value. assert_eq!( @@ -719,9 +629,7 @@ mod tests { // Get the controller from the proof service property in the Sidetree-resolved DID document. let proof_service = get_proof_service(&did_doc).unwrap(); - let controller = - get_from_proof_service(proof_service, "controller") - .unwrap(); + let controller = get_from_proof_service(proof_service, "controller").unwrap(); // Transform the DID document by resolving into Trustchain format. let actual = transform_doc(&did_doc, controller.as_str()); @@ -762,11 +670,8 @@ mod tests { property_set: None, }; - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Call function and get output result type - let output = resolver.transform_as_result(input_res_meta, input_doc, input_doc_meta); + let output = transform_as_result(input_res_meta, input_doc, input_doc_meta); // Result should be Ok variant with returned data if let Ok((actual_output_res_meta, actual_output_doc, actual_output_doc_meta)) = output { @@ -805,11 +710,8 @@ mod tests { property_set: None, }; - // Construct a Resolver instance. - let resolver = Resolver::new(get_http_resolver()); - // Call the resolve function and get output Result type. - let output = resolver.transform_as_result(input_res_meta, input_doc, input_doc_meta); + let output = transform_as_result(input_res_meta, input_doc, input_doc_meta); // Check for the correct error. assert!(matches!( diff --git a/trustchain-core/src/verifier.rs b/trustchain-core/src/verifier.rs index a985558c..b66e71f2 100644 --- a/trustchain-core/src/verifier.rs +++ b/trustchain-core/src/verifier.rs @@ -3,7 +3,7 @@ use std::error::Error; use crate::chain::{Chain, ChainError, DIDChain}; use crate::commitment::{CommitmentError, DIDCommitment, TimestampCommitment}; -use crate::resolver::{Resolver, ResolverError}; +use crate::resolver::{ResolverError, TrustchainResolver}; use async_trait::async_trait; use ssi::did_resolve::DIDResolver; use thiserror::Error; @@ -200,9 +200,10 @@ pub trait Verifier { &self, did: &str, root_timestamp: Timestamp, + resolver: &dyn TrustchainResolver, ) -> Result { // Build a chain from the given DID to the root. - let resolver = self.resolver(); + // let resolver = self.resolver(); let chain = DIDChain::new(did, resolver).await?; // Verify the proofs in the chain. @@ -247,7 +248,7 @@ pub trait Verifier { fn validate_pow_hash(&self, hash: &str) -> Result<(), VerifierError>; /// Gets the resolver used for DID verification. - fn resolver(&self) -> &Resolver; + fn resolver(&self) -> &dyn TrustchainResolver; } #[cfg(test)] diff --git a/trustchain-ion/src/lib.rs b/trustchain-ion/src/lib.rs index 590b8509..df9ce87f 100644 --- a/trustchain-ion/src/lib.rs +++ b/trustchain-ion/src/lib.rs @@ -8,6 +8,7 @@ pub mod create; pub mod data; pub mod ion; pub mod mnemonic; +pub mod resolver; pub mod root; pub mod sidetree; pub mod utils; diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs new file mode 100644 index 00000000..5e1aa354 --- /dev/null +++ b/trustchain-ion/src/resolver.rs @@ -0,0 +1,100 @@ +//! DID resolution and `DIDResolver` implementation. +use crate::utils::HasEndpoints; +use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; +use async_trait::async_trait; +use did_method_key::DIDKey; +use serde_json::Value; +use ssi::did::{DIDMethod, Document, Service, ServiceEndpoint}; +use ssi::did_resolve::{ + DIDResolver, DocumentMetadata, Metadata, ResolutionInputMetadata, ResolutionMetadata, +}; +use ssi::one_or_many::OneOrMany; +use std::collections::HashMap; +use thiserror::Error; + +use ssi::{ + did::DIDMethod, + did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, +}; +use trustchain_core::resolver::TrustchainResolver; + +// Newtype pattern (workaround for lack of trait upcasting coercion). +// Specifically, the DIDMethod method to_resolver() returns a reference but we want ownership. +// The workaround is to define a wrapper for DIDMethod that implements DIDResolver. +// See https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types. +pub struct DIDMethodWrapper(pub S); + +#[async_trait] +impl DIDResolver for DIDMethodWrapper { + async fn resolve( + &self, + did: &str, + input_metadata: &ResolutionInputMetadata, + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + self.0.to_resolver().resolve(did, input_metadata).await + } +} + +/// Struct for performing resolution from a sidetree server to generate +/// Trustchain DID document and DID document metadata. +pub struct Resolver { + pub wrapped_resolver: T, +} + +impl Resolver { + /// Constructs a Trustchain resolver. + pub fn new(resolver: T) -> Self { + Self { + wrapped_resolver: resolver, + } + } + /// Constructs a Trustchain resolver from a DIDMethod. + pub fn from(method: S) -> Resolver> { + // Wrap the DIDMethod. + Resolver::>::new(DIDMethodWrapper::(method)) + } +} + +impl TrustchainResolver for Resolver +where + T: DIDResolver + Sync + Send, +{ + // use default impls on the trait def + // (grants .transform method) +} + +#[async_trait] +impl DIDResolver for Resolver +where + T: DIDResolver + Sync + Send, +{ + async fn resolve( + &self, + did: &str, + input_metadata: &ResolutionInputMetadata, + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + // TODO: remove upon handling with DIDMethods + if did.starts_with("did:key:") { + let did_key_resolver = DIDKey; + return did_key_resolver + .resolve(did, &ResolutionInputMetadata::default()) + .await; + } + // let ion_resolver = self.wrapped_resolver; + // let resolved = ion_resolver.resolve(did, input_metadata).await; + + let resolved = self.wrapped_resolver.resolve(did, input_metadata).await; + + // Consider using ResolutionInputMetadata to optionally not perform transform. + // Resolve with the wrapped DIDResolver and then transform to Trustchain format. + self.transform(resolved) + } +} From d8900e6fd68d5b81ee838e600eea8b7a812bef32 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Fri, 10 Nov 2023 18:04:19 +0000 Subject: [PATCH 03/17] Remove generic from DIDChain --- trustchain-api/src/api.rs | 3 ++- trustchain-core/src/chain.rs | 11 ++--------- trustchain-core/src/resolver.rs | 4 +--- trustchain-core/src/verifier.rs | 3 +-- trustchain-ion/Cargo.toml | 1 + trustchain-ion/src/lib.rs | 3 ++- trustchain-ion/src/resolver.rs | 14 ++------------ trustchain-ion/src/verifier.rs | 8 ++++---- 8 files changed, 15 insertions(+), 32 deletions(-) diff --git a/trustchain-api/src/api.rs b/trustchain-api/src/api.rs index 2609aece..d84f7960 100644 --- a/trustchain-api/src/api.rs +++ b/trustchain-api/src/api.rs @@ -14,13 +14,14 @@ use trustchain_core::{ chain::DIDChain, holder::Holder, issuer::{Issuer, IssuerError}, - resolver::{Resolver, ResolverResult, TrustchainResolver}, + resolver::{ResolverResult, TrustchainResolver}, vc::CredentialError, verifier::{Timestamp, Verifier, VerifierError}, vp::PresentationError, }; use trustchain_ion::{ attest::attest_operation, attestor::IONAttestor, create::create_operation, get_ion_resolver, + resolver::Resolver, }; /// API for Trustchain CLI DID functionality. diff --git a/trustchain-core/src/chain.rs b/trustchain-core/src/chain.rs index 01f7671c..ec7f7344 100644 --- a/trustchain-core/src/chain.rs +++ b/trustchain-core/src/chain.rs @@ -4,11 +4,7 @@ use crate::resolver::TrustchainResolver; use crate::utils::{canonicalize, decode, decode_verify, extract_keys, hash}; use serde::{Deserialize, Serialize}; use ssi::did_resolve::Metadata; -use ssi::{ - did::Document, - did_resolve::{DIDResolver, DocumentMetadata}, - one_or_many::OneOrMany, -}; +use ssi::{did::Document, did_resolve::DocumentMetadata, one_or_many::OneOrMany}; use std::collections::HashMap; use std::fmt; use thiserror::Error; @@ -125,10 +121,7 @@ impl fmt::Display for DIDChain { impl DIDChain { // Public constructor. - pub async fn new( - did: &str, - resolver: &dyn TrustchainResolver, - ) -> Result { + pub async fn new(did: &str, resolver: &dyn TrustchainResolver) -> Result { // Construct an empty chain. let mut chain = DIDChain::empty(); diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index dde719ac..cb6a4ea5 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -1,10 +1,8 @@ //! DID resolution and `DIDResolver` implementation. -use crate::utils::HasEndpoints; use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; use async_trait::async_trait; -use did_method_key::DIDKey; use serde_json::Value; -use ssi::did::{DIDMethod, Document, Service, ServiceEndpoint}; +use ssi::did::{Document, Service, ServiceEndpoint}; use ssi::did_resolve::{ DIDResolver, DocumentMetadata, Metadata, ResolutionInputMetadata, ResolutionMetadata, }; diff --git a/trustchain-core/src/verifier.rs b/trustchain-core/src/verifier.rs index b66e71f2..ee4e2352 100644 --- a/trustchain-core/src/verifier.rs +++ b/trustchain-core/src/verifier.rs @@ -200,10 +200,9 @@ pub trait Verifier { &self, did: &str, root_timestamp: Timestamp, - resolver: &dyn TrustchainResolver, ) -> Result { // Build a chain from the given DID to the root. - // let resolver = self.resolver(); + let resolver = self.resolver(); let chain = DIDChain::new(did, resolver).await?; // Verify the proofs in the chain. diff --git a/trustchain-ion/Cargo.toml b/trustchain-ion/Cargo.toml index 03859bcd..28d1150b 100644 --- a/trustchain-ion/Cargo.toml +++ b/trustchain-ion/Cargo.toml @@ -16,6 +16,7 @@ bitcoincore-rpc = "0.16.0" canonical_json = "0.4.0" chrono = "0.4" clap = { version = "^4.1", features=["derive", "cargo"] } +did-method-key = {git="https://github.com/alan-turing-institute/ssi.git", branch="modify-encode-sign-jwt"} did-ion = {git="https://github.com/alan-turing-institute/ssi.git", branch="modify-encode-sign-jwt"} ed25519-dalek-bip32 = "0.3.0" flate2 = "1.0.24" diff --git a/trustchain-ion/src/lib.rs b/trustchain-ion/src/lib.rs index df9ce87f..deea4281 100644 --- a/trustchain-ion/src/lib.rs +++ b/trustchain-ion/src/lib.rs @@ -15,12 +15,13 @@ pub mod utils; pub mod verifier; use crate::ion::IONTest as ION; +use crate::resolver::{DIDMethodWrapper, Resolver}; use did_ion::sidetree::SidetreeClient; use serde::{Deserialize, Serialize}; use std::{io, num::ParseIntError}; use thiserror::Error; -use trustchain_core::resolver::{DIDMethodWrapper, Resolver}; +// TODO: remove this type alias /// Type alias pub type IONResolver = Resolver>>; diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index 5e1aa354..ca950b6b 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,19 +1,9 @@ //! DID resolution and `DIDResolver` implementation. -use crate::utils::HasEndpoints; -use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; use async_trait::async_trait; use did_method_key::DIDKey; -use serde_json::Value; -use ssi::did::{DIDMethod, Document, Service, ServiceEndpoint}; -use ssi::did_resolve::{ - DIDResolver, DocumentMetadata, Metadata, ResolutionInputMetadata, ResolutionMetadata, -}; -use ssi::one_or_many::OneOrMany; -use std::collections::HashMap; -use thiserror::Error; - +use ssi::did_resolve::DocumentMetadata; use ssi::{ - did::DIDMethod, + did::{DIDMethod, Document}, did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, }; use trustchain_core::resolver::TrustchainResolver; diff --git a/trustchain-ion/src/verifier.rs b/trustchain-ion/src/verifier.rs index b8414aa3..3474ac4c 100644 --- a/trustchain-ion/src/verifier.rs +++ b/trustchain-ion/src/verifier.rs @@ -21,14 +21,14 @@ use ssi::did::Document; use ssi::did_resolve::{DIDResolver, DocumentMetadata}; use std::collections::HashMap; +use crate::resolver::Resolver; use std::marker::PhantomData; use std::str::FromStr; use std::sync::{Arc, Mutex}; use trustchain_core::commitment::{ CommitmentChain, CommitmentError, DIDCommitment, TimestampCommitment, }; -use trustchain_core::resolver::{Resolver, ResolverError, TrustchainResolver}; - +use trustchain_core::resolver::{ResolverError, TrustchainResolver}; use trustchain_core::verifier::{Timestamp, VerifiableTimestamp, Verifier, VerifierError}; /// Data bundle for DID timestamp verification. @@ -433,7 +433,7 @@ where Ok(construct_commitment(bundle).map(Box::new)?) } - fn resolver(&self) -> &Resolver { + fn resolver(&self) -> &dyn TrustchainResolver { &self.resolver } @@ -494,7 +494,7 @@ where Ok(construct_commitment(bundle).map(Box::new)?) } - fn resolver(&self) -> &Resolver { + fn resolver(&self) -> &dyn TrustchainResolver { &self.resolver } From 92f1ec52728af47398b616bb803606c6dc39aeb3 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Mon, 13 Nov 2023 09:04:11 +0000 Subject: [PATCH 04/17] Refactor with trait object --- trustchain-api/src/api.rs | 20 ++++++++-------- trustchain-cli/src/bin/main.rs | 2 +- trustchain-core/src/issuer.rs | 6 ++--- trustchain-core/src/resolver.rs | 41 ++++++++++----------------------- trustchain-http/src/issuer.rs | 17 ++++++++------ trustchain-http/src/resolver.rs | 10 ++++---- trustchain-http/src/state.rs | 3 ++- trustchain-http/src/verifier.rs | 6 ++++- trustchain-ion/src/attestor.rs | 7 +++--- 9 files changed, 52 insertions(+), 60 deletions(-) diff --git a/trustchain-api/src/api.rs b/trustchain-api/src/api.rs index d84f7960..0f3d02e4 100644 --- a/trustchain-api/src/api.rs +++ b/trustchain-api/src/api.rs @@ -21,7 +21,6 @@ use trustchain_core::{ }; use trustchain_ion::{ attest::attest_operation, attestor::IONAttestor, create::create_operation, get_ion_resolver, - resolver::Resolver, }; /// API for Trustchain CLI DID functionality. @@ -43,10 +42,7 @@ pub trait TrustchainDIDAPI { attest_operation(did, controlled_did, verbose).await } /// Resolves a given DID using given endpoint. - async fn resolve(did: &str, resolver: &Resolver) -> ResolverResult - where - T: DIDResolver + Send + Sync, - { + async fn resolve(did: &str, resolver: &dyn TrustchainResolver) -> ResolverResult { // Result metadata, Document, Document metadata resolver.resolve_as_result(did).await } @@ -87,12 +83,12 @@ pub trait TrustchainDIDAPI { #[async_trait] pub trait TrustchainVCAPI { /// Signs a credential. - async fn sign( + async fn sign( mut credential: Credential, did: &str, linked_data_proof_options: Option, key_id: Option<&str>, - resolver: &T, + resolver: &dyn TrustchainResolver, context_loader: &mut ContextLoader, ) -> Result { credential.issuer = Some(ssi::vc::Issuer::URI(URI::String(did.to_string()))); @@ -124,7 +120,7 @@ pub trait TrustchainVCAPI { let result = credential .verify( linked_data_proof_options, - verifier.resolver(), + verifier.resolver().as_did_resolver(), context_loader, ) .await; @@ -209,7 +205,7 @@ pub trait TrustchainVPAPI { match Credential::decode_verify_jwt( jwt, ldp_opts.clone(), - verifier.resolver(), + verifier.resolver().as_did_resolver(), &mut context_loader, ) .await @@ -235,7 +231,11 @@ pub trait TrustchainVPAPI { // Verify signature by holder to authenticate let result = presentation - .verify(ldp_options.clone(), verifier.resolver(), context_loader) + .verify( + ldp_options.clone(), + verifier.resolver().as_did_resolver(), + context_loader, + ) .await; if !result.errors.is_empty() { return Err(PresentationError::VerifiedHolderUnauthenticated(result)); diff --git a/trustchain-cli/src/bin/main.rs b/trustchain-cli/src/bin/main.rs index 8d229c11..b8ceb029 100644 --- a/trustchain-cli/src/bin/main.rs +++ b/trustchain-cli/src/bin/main.rs @@ -11,7 +11,7 @@ use trustchain_api::{ TrustchainAPI, }; use trustchain_cli::config::cli_config; -use trustchain_core::{resolver::TrustchainResolver, vc::CredentialError, verifier::Verifier}; +use trustchain_core::{vc::CredentialError, verifier::Verifier}; use trustchain_ion::{ attest::attest_operation, create::{create_operation, create_operation_mnemonic}, diff --git a/trustchain-core/src/issuer.rs b/trustchain-core/src/issuer.rs index 21811d89..d6ef46dd 100644 --- a/trustchain-core/src/issuer.rs +++ b/trustchain-core/src/issuer.rs @@ -1,8 +1,8 @@ //! DID issuer API. use crate::key_manager::KeyManagerError; +use crate::resolver::TrustchainResolver; use crate::subject::Subject; use async_trait::async_trait; -use ssi::did_resolve::DIDResolver; use ssi::jsonld::ContextLoader; use ssi::vc::{Credential, LinkedDataProofOptions}; use thiserror::Error; @@ -43,12 +43,12 @@ impl From for IssuerError { #[async_trait] pub trait Issuer: Subject { /// Signs a credential. An issuer attests to a credential by signing the credential with one of their private signing keys. - async fn sign( + async fn sign( &self, credential: &Credential, linked_data_proof_options: Option, key_id: Option<&str>, - resolver: &T, + resolver: &dyn TrustchainResolver, context_loader: &mut ContextLoader, ) -> Result; } diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index cb6a4ea5..499b52b6 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -228,28 +228,7 @@ fn transform_as_result( /// Trait for performing Trustchain resolution. #[async_trait] -pub trait TrustchainResolver: DIDResolver { - // async fn resolve( - // &self, - // did: &str, - // input_metadata: &ResolutionInputMetadata, - // ) -> ( - // ResolutionMetadata, - // Option, - // Option, - // ) { - // // TODO: remove upon handling with DIDMethods - // if did.starts_with("did:key:") { - // let did_key_resolver = DIDKey; - // return did_key_resolver - // .resolve(did, &ResolutionInputMetadata::default()) - // .await; - // } - // // Consider using ResolutionInputMetadata to optionally not perform transform. - // // Resolve with the wrapped DIDResolver and then transform to Trustchain format. - // self.transform(self.resolve(did, input_metadata).await) - // } - +pub trait TrustchainResolver: DIDResolver + AsDIDResolver { /// Transforms the result of a DID resolution into the Trustchain format. fn transform( &self, @@ -343,6 +322,17 @@ pub trait TrustchainResolver: DIDResolver { } } +// To facilitate trait upcasting: https://stackoverflow.com/a/28664881 +pub trait AsDIDResolver { + fn as_did_resolver(&self) -> &dyn DIDResolver; +} + +impl AsDIDResolver for T { + fn as_did_resolver(&self) -> &dyn DIDResolver { + self + } +} + #[cfg(test)] mod tests { use super::*; @@ -352,14 +342,7 @@ mod tests { TEST_SIDETREE_DOCUMENT_SERVICE_NOT_PROOF, TEST_SIDETREE_DOCUMENT_WITH_CONTROLLER, TEST_TRUSTCHAIN_DOCUMENT, TEST_TRUSTCHAIN_DOCUMENT_METADATA, }; - use crate::utils::canonicalize; - use ssi::did_resolve::HTTPDIDResolver; - - // General function for generating a HTTP resolver for tests only - fn get_http_resolver() -> HTTPDIDResolver { - HTTPDIDResolver::new("http://localhost:3000/") - } #[test] fn test_add_controller() { diff --git a/trustchain-http/src/issuer.rs b/trustchain-http/src/issuer.rs index 379a937f..5c71c932 100644 --- a/trustchain-http/src/issuer.rs +++ b/trustchain-http/src/issuer.rs @@ -10,14 +10,13 @@ use axum::Json; use chrono::Utc; use log::info; use serde::{Deserialize, Serialize}; -use ssi::did_resolve::DIDResolver; use ssi::jsonld::ContextLoader; use ssi::one_or_many::OneOrMany; use ssi::vc::Credential; use ssi::vc::VCDateTime; use std::sync::Arc; use trustchain_core::issuer::Issuer; -use trustchain_core::resolver::Resolver; +use trustchain_core::resolver::TrustchainResolver; use trustchain_core::verifier::Verifier; use trustchain_ion::attestor::IONAttestor; @@ -66,11 +65,11 @@ pub trait TrustchainIssuerHTTP { issuer_did: &str, ) -> CredentialOffer; /// Issues a verifiable credential (should it return `Credential` or `String`) - async fn issue_credential( + async fn issue_credential( credential: &Credential, subject_id: Option<&str>, issuer_did: &str, - resolver: &Resolver, + resolver: &dyn TrustchainResolver, ) -> Result; } @@ -91,11 +90,11 @@ impl TrustchainIssuerHTTP for TrustchainIssuerHTTPHandler { CredentialOffer::generate(&credential, id) } - async fn issue_credential( + async fn issue_credential( credential: &Credential, subject_id: Option<&str>, issuer_did: &str, - resolver: &Resolver, + resolver: &dyn TrustchainResolver, ) -> Result { let mut credential = credential.to_owned(); credential.issuer = Some(ssi::vc::Issuer::URI(ssi::vc::URI::String( @@ -332,7 +331,11 @@ mod tests { // Test signature let verifier = IONVerifier::new(get_ion_resolver("http://localhost:3000/")); let verify_credential_result = credential - .verify(None, verifier.resolver(), &mut ContextLoader::default()) + .verify( + None, + verifier.resolver().as_did_resolver(), + &mut ContextLoader::default(), + ) .await; assert!(verify_credential_result.errors.is_empty()); diff --git a/trustchain-http/src/resolver.rs b/trustchain-http/src/resolver.rs index b247b274..cc92c313 100644 --- a/trustchain-http/src/resolver.rs +++ b/trustchain-http/src/resolver.rs @@ -14,7 +14,7 @@ use ssi::{ }; use std::sync::Arc; use trustchain_core::chain::{Chain, DIDChain}; -use trustchain_core::resolver::{Resolver, TrustchainResolver}; +use trustchain_core::resolver::TrustchainResolver; use trustchain_core::verifier::{Timestamp, Verifier, VerifierError}; use trustchain_ion::verifier::{IONVerifier, VerificationBundle}; @@ -22,9 +22,9 @@ use trustchain_ion::verifier::{IONVerifier, VerificationBundle}; #[async_trait] pub trait TrustchainHTTP { /// Resolves a DID document. - async fn resolve_did( + async fn resolve_did( did: &str, - resolver: &Resolver, + resolver: &dyn TrustchainResolver, ) -> Result; /// Resolves a DID chain. @@ -46,9 +46,9 @@ pub struct TrustchainHTTPHandler {} #[async_trait] impl TrustchainHTTP for TrustchainHTTPHandler { - async fn resolve_did( + async fn resolve_did( did: &str, - resolver: &Resolver, + resolver: &dyn TrustchainResolver, ) -> Result { debug!("Resolving..."); let result = resolver.resolve_as_result(did).await?; diff --git a/trustchain-http/src/state.rs b/trustchain-http/src/state.rs index 10248254..a3ff59d5 100644 --- a/trustchain-http/src/state.rs +++ b/trustchain-http/src/state.rs @@ -4,7 +4,8 @@ use chrono::NaiveDate; use ssi::vc::Credential; use std::collections::HashMap; use std::sync::RwLock; -use trustchain_core::{resolver::Resolver, TRUSTCHAIN_DATA}; +use trustchain_core::TRUSTCHAIN_DATA; +use trustchain_ion::resolver::Resolver; use trustchain_ion::{get_ion_resolver, verifier::IONVerifier, IONResolver}; const DEFAULT_VERIFIER_ENDPOINT: &str = "http://localhost:3000/"; diff --git a/trustchain-http/src/verifier.rs b/trustchain-http/src/verifier.rs index d2477987..e9e0991e 100644 --- a/trustchain-http/src/verifier.rs +++ b/trustchain-http/src/verifier.rs @@ -51,7 +51,11 @@ pub trait TrustchainVerifierHTTP { verifier: &IONVerifier, ) -> Result<(), TrustchainHTTPError> { let verify_credential_result = credential - .verify(None, verifier.resolver(), &mut ContextLoader::default()) + .verify( + None, + verifier.resolver().as_did_resolver(), + &mut ContextLoader::default(), + ) .await; if !verify_credential_result.errors.is_empty() { return Err(TrustchainHTTPError::InvalidSignature); diff --git a/trustchain-ion/src/attestor.rs b/trustchain-ion/src/attestor.rs index 958a2eab..ce420ac8 100644 --- a/trustchain-ion/src/attestor.rs +++ b/trustchain-ion/src/attestor.rs @@ -11,6 +11,7 @@ use std::convert::TryFrom; use trustchain_core::holder::{Holder, HolderError}; use trustchain_core::issuer::{Issuer, IssuerError}; use trustchain_core::key_manager::KeyType; +use trustchain_core::resolver::TrustchainResolver; use trustchain_core::{ attestor::{Attestor, AttestorError}, key_manager::{AttestorKeyManager, KeyManager, KeyManagerError}, @@ -144,12 +145,12 @@ impl Attestor for IONAttestor { #[async_trait] impl Issuer for IONAttestor { // Attests to a given credential returning the credential with proof. The `@context` of the credential has linked-data fields strictly checked as part of proof generation. - async fn sign( + async fn sign( &self, credential: &Credential, linked_data_proof_options: Option, key_id: Option<&str>, - resolver: &T, + resolver: &dyn TrustchainResolver, context_loader: &mut ContextLoader, ) -> Result { // Get the signing key. @@ -160,7 +161,7 @@ impl Issuer for IONAttestor { .generate_proof( &signing_key, &linked_data_proof_options.unwrap_or(LinkedDataProofOptions::default()), - resolver, + resolver.as_did_resolver(), context_loader, ) .await?; From 0c43636a0afe82cef663a6fd4eebda5f3cb4940d Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Mon, 13 Nov 2023 11:34:44 +0000 Subject: [PATCH 05/17] Default trustchain_resolve method in trait and add extended_transform --- trustchain-core/src/resolver.rs | 49 +++++++++++++++++++++++++++++++++ trustchain-ion/src/resolver.rs | 24 ++++------------ 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index 499b52b6..b8fb3bf1 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -1,6 +1,7 @@ //! DID resolution and `DIDResolver` implementation. use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE; use async_trait::async_trait; +use did_method_key::DIDKey; use serde_json::Value; use ssi::did::{Document, Service, ServiceEndpoint}; use ssi::did_resolve::{ @@ -229,6 +230,10 @@ fn transform_as_result( /// Trait for performing Trustchain resolution. #[async_trait] pub trait TrustchainResolver: DIDResolver + AsDIDResolver { + /// Provides the wrapped resolver of the implementing type. + // fn wrapped_resolver(&self) -> &T; + fn wrapped_resolver(&self) -> &dyn DIDResolver; + /// Transforms the result of a DID resolution into the Trustchain format. fn transform( &self, @@ -320,6 +325,50 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { Ok((did_res_meta, did_doc, did_doc_meta)) } } + + async fn trustchain_resolve( + &self, + did: &str, + input_metadata: &ResolutionInputMetadata, + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + // TODO: remove upon handling with DIDMethods + if did.starts_with("did:key:") { + let did_key_resolver = DIDKey; + return did_key_resolver + .resolve(did, &ResolutionInputMetadata::default()) + .await; + } + // let ion_resolver = self.wrapped_resolver; + // let resolved = ion_resolver.resolve(did, input_metadata).await; + + let resolved = self.wrapped_resolver().resolve(did, input_metadata).await; + + // Consider using ResolutionInputMetadata to optionally not perform transform. + // Resolve with the wrapped DIDResolver and then transform to Trustchain format. + let transformed = self.transform(resolved); + self.extended_transform(transformed) + } + + /// Provides implementors of this trait with a mechanism to perform additional transformations + /// when resolving DIDs. By default this is the identity map (no transformations). + fn extended_transform( + &self, + (res_meta, doc, doc_meta): ( + ResolutionMetadata, + Option, + Option, + ), + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + (res_meta, doc, doc_meta) + } } // To facilitate trait upcasting: https://stackoverflow.com/a/28664881 diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index ca950b6b..10144e68 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,6 +1,5 @@ //! DID resolution and `DIDResolver` implementation. use async_trait::async_trait; -use did_method_key::DIDKey; use ssi::did_resolve::DocumentMetadata; use ssi::{ did::{DIDMethod, Document}, @@ -53,8 +52,11 @@ impl TrustchainResolver for Resolver where T: DIDResolver + Sync + Send, { - // use default impls on the trait def - // (grants .transform method) + fn wrapped_resolver(&self) -> &dyn DIDResolver { + &self.wrapped_resolver + } + + // fn extended_transform() {} } #[async_trait] @@ -71,20 +73,6 @@ where Option, Option, ) { - // TODO: remove upon handling with DIDMethods - if did.starts_with("did:key:") { - let did_key_resolver = DIDKey; - return did_key_resolver - .resolve(did, &ResolutionInputMetadata::default()) - .await; - } - // let ion_resolver = self.wrapped_resolver; - // let resolved = ion_resolver.resolve(did, input_metadata).await; - - let resolved = self.wrapped_resolver.resolve(did, input_metadata).await; - - // Consider using ResolutionInputMetadata to optionally not perform transform. - // Resolve with the wrapped DIDResolver and then transform to Trustchain format. - self.transform(resolved) + self.trustchain_resolve(did, input_metadata).await } } From 99aca69f8115e5c4d8041181b9bd68d9759f9964 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Mon, 13 Nov 2023 12:35:45 +0000 Subject: [PATCH 06/17] Begin implementing the ipfs resolution --- trustchain-core/src/resolver.rs | 10 +---- trustchain-ion/src/lib.rs | 3 ++ trustchain-ion/src/resolver.rs | 73 ++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index b8fb3bf1..f819341b 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -50,12 +50,6 @@ pub type ResolverResult = Result< ResolverError, >; -// pub trait CASClient { -// fn client() -> Option String> { -// None -// } -// } - /// Adds the controller property to a resolved DID document, using the /// value passed in the controller_did argument. This must be the DID of /// the subject (id property) found in the upstream DID document. @@ -350,12 +344,12 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { // Consider using ResolutionInputMetadata to optionally not perform transform. // Resolve with the wrapped DIDResolver and then transform to Trustchain format. let transformed = self.transform(resolved); - self.extended_transform(transformed) + self.extended_transform(transformed).await } /// Provides implementors of this trait with a mechanism to perform additional transformations /// when resolving DIDs. By default this is the identity map (no transformations). - fn extended_transform( + async fn extended_transform( &self, (res_meta, doc, doc_meta): ( ResolutionMetadata, diff --git a/trustchain-ion/src/lib.rs b/trustchain-ion/src/lib.rs index deea4281..c9047ee2 100644 --- a/trustchain-ion/src/lib.rs +++ b/trustchain-ion/src/lib.rs @@ -176,3 +176,6 @@ pub const MIN_POW_ZEROS: usize = 14; pub const SIGNING_KEY_DERIVATION_PATH: &str = "m/0h"; pub const UPDATE_KEY_DERIVATION_PATH: &str = "m/1h"; pub const RECOVERY_KEY_DERIVATION_PATH: &str = "m/2h"; + +// IPFS KEY +pub const SERVICE_TYPE_IPFS_KEY: &str = "IPFSKey"; diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index 10144e68..d1d6bf40 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,11 +1,18 @@ //! DID resolution and `DIDResolver` implementation. use async_trait::async_trait; +use ipfs_api_backend_hyper::IpfsClient; +use ssi::did::{Service, ServiceEndpoint}; use ssi::did_resolve::DocumentMetadata; +use ssi::one_or_many::OneOrMany; use ssi::{ did::{DIDMethod, Document}, did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, }; use trustchain_core::resolver::TrustchainResolver; +use trustchain_core::utils::{HasEndpoints, HasKeys}; + +use crate::utils::query_ipfs; +use crate::SERVICE_TYPE_IPFS_KEY; // Newtype pattern (workaround for lack of trait upcasting coercion). // Specifically, the DIDMethod method to_resolver() returns a reference but we want ownership. @@ -32,6 +39,7 @@ impl DIDResolver for DIDMethodWrapper { /// Trustchain DID document and DID document metadata. pub struct Resolver { pub wrapped_resolver: T, + // pub ipfs_client: IpfsClient, } impl Resolver { @@ -39,6 +47,7 @@ impl Resolver { pub fn new(resolver: T) -> Self { Self { wrapped_resolver: resolver, + // ipfs_client: IpfsClient::default(), } } /// Constructs a Trustchain resolver from a DIDMethod. @@ -48,6 +57,7 @@ impl Resolver { } } +#[async_trait] impl TrustchainResolver for Resolver where T: DIDResolver + Sync + Send, @@ -56,9 +66,70 @@ where &self.wrapped_resolver } - // fn extended_transform() {} + async fn extended_transform( + &self, + (res_meta, doc, doc_meta): ( + ResolutionMetadata, + Option, + Option, + ), + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + // Iterate over any endpoints of type IPFSKey + + // let endpoints = doc.unwrap().get_endpoints().unwrap(); + // TODO: fix refs + // let ipfs_key_endpoints: Vec = doc + // .unwrap() + // .service + // .unwrap() + // .iter() + // .filter(|s| s.type_.to_single().is_some()) + // .filter_map(|s| { + // if s.type_ + // .to_single() + // .as_deref() + // .unwrap() + // .eq(SERVICE_TYPE_IPFS_KEY) + // { + // match s.service_endpoint { + // Some(OneOrMany::One(ServiceEndpoint::URI(uri))) => Some(uri), + // _ => None, + // } + // } else { + // None + // } + // }) + // .collect(); + + // TODO: move to Resolver struct. + // let ipfs_client = IpfsClient::default(); + // for endpoint in ipfs_key_endpoints { + // // Download the content of the corresponding CID + // let result = query_ipfs(cid, &ipfs_client).await.unwrap(); + + // // Check the content is a valid public key block. + + // // Insert the public key in the list of keys inside the resolved DID document. + // } + + // Duplication? + // // Check if the Trustchain proof service alreday exists in the document. + // let doc_clone = self.remove_proof_service(doc_clone); + + (res_meta, doc, doc_meta) + } } +// pub trait CASClient { +// fn client() -> Option String> { +// None +// } +// } + #[async_trait] impl DIDResolver for Resolver where From 9a4a3c99a75f0e1de8a6dc0a27f31def00b1a73b Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Mon, 13 Nov 2023 16:01:55 +0000 Subject: [PATCH 07/17] Fix references --- trustchain-ion/src/resolver.rs | 51 ++++++++++++++++------------------ 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index d1d6bf40..3d6b575c 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -78,32 +78,28 @@ where Option, Option, ) { - // Iterate over any endpoints of type IPFSKey - - // let endpoints = doc.unwrap().get_endpoints().unwrap(); - // TODO: fix refs - // let ipfs_key_endpoints: Vec = doc - // .unwrap() - // .service - // .unwrap() - // .iter() - // .filter(|s| s.type_.to_single().is_some()) - // .filter_map(|s| { - // if s.type_ - // .to_single() - // .as_deref() - // .unwrap() - // .eq(SERVICE_TYPE_IPFS_KEY) - // { - // match s.service_endpoint { - // Some(OneOrMany::One(ServiceEndpoint::URI(uri))) => Some(uri), - // _ => None, - // } - // } else { - // None - // } - // }) - // .collect(); + let ipfs_key_endpoints: Vec = doc + .unwrap() + .service + .unwrap() + .iter() + .filter(|s| s.type_.to_single().is_some()) + .filter_map(|ref s| { + if s.type_ + .to_single() + .as_deref() + .unwrap() + .eq(SERVICE_TYPE_IPFS_KEY) + { + match s.service_endpoint { + Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()), + _ => None, + } + } else { + None + } + }) + .collect(); // TODO: move to Resolver struct. // let ipfs_client = IpfsClient::default(); @@ -120,7 +116,8 @@ where // // Check if the Trustchain proof service alreday exists in the document. // let doc_clone = self.remove_proof_service(doc_clone); - (res_meta, doc, doc_meta) + // TODO. + (res_meta, None, doc_meta) } } From 8051b931b0e99af84bde6781d02e1d24f41f9952 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Tue, 14 Nov 2023 09:20:15 +0000 Subject: [PATCH 08/17] Mimic trustchain-core structure; add unit tests --- trustchain-core/src/resolver.rs | 4 - trustchain-ion/src/resolver.rs | 324 ++++++++++++++++++++++++++------ 2 files changed, 265 insertions(+), 63 deletions(-) diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index f819341b..250bd3ef 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -165,10 +165,6 @@ fn transform_doc(doc: &Document, controller_did: &str) -> Document { // Clone the passed DID document. let doc_clone = doc.clone(); - // // TODO: CAS keys - // // Add any keys from IPFS - // let doc_clone = self.add_cas_keys(doc_clone); - // Duplication? // // Check if the Trustchain proof service alreday exists in the document. // let doc_clone = self.remove_proof_service(doc_clone); diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index 3d6b575c..ef9a7889 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,17 +1,17 @@ //! DID resolution and `DIDResolver` implementation. use async_trait::async_trait; use ipfs_api_backend_hyper::IpfsClient; -use ssi::did::{Service, ServiceEndpoint}; +use serde_json::from_str; +use ssi::did::{ServiceEndpoint, VerificationMethod}; use ssi::did_resolve::DocumentMetadata; use ssi::one_or_many::OneOrMany; use ssi::{ did::{DIDMethod, Document}, did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, }; -use trustchain_core::resolver::TrustchainResolver; -use trustchain_core::utils::{HasEndpoints, HasKeys}; +use trustchain_core::resolver::{TrustchainResolver, ResolverError}; -use crate::utils::query_ipfs; +use crate::utils::{query_ipfs, decode_ipfs_content}; use crate::SERVICE_TYPE_IPFS_KEY; // Newtype pattern (workaround for lack of trait upcasting coercion). @@ -57,6 +57,24 @@ impl Resolver { } } +#[async_trait] +impl DIDResolver for Resolver +where + T: DIDResolver + Sync + Send, +{ + async fn resolve( + &self, + did: &str, + input_metadata: &ResolutionInputMetadata, + ) -> ( + ResolutionMetadata, + Option, + Option, + ) { + self.trustchain_resolve(did, input_metadata).await + } +} + #[async_trait] impl TrustchainResolver for Resolver where @@ -78,69 +96,257 @@ where Option, Option, ) { - let ipfs_key_endpoints: Vec = doc - .unwrap() - .service - .unwrap() - .iter() - .filter(|s| s.type_.to_single().is_some()) - .filter_map(|ref s| { - if s.type_ - .to_single() - .as_deref() - .unwrap() - .eq(SERVICE_TYPE_IPFS_KEY) - { - match s.service_endpoint { - Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()), - _ => None, - } - } else { - None + // If a document and document metadata are returned, try to convert + if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) { + // Convert to trustchain versions + let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta).await; + match tc_result { + // Map the tuple of non-option types to have tuple with optional document + // document metadata + Ok((tc_res_meta, tc_doc, tc_doc_meta)) => { + (tc_res_meta, Some(tc_doc), Some(tc_doc_meta)) } - }) - .collect(); + // If cannot convert, return the relevant error + Err(ResolverError::FailedToConvertToTrustchain) => { + let res_meta = ResolutionMetadata { + error: Some( + "Failed to convert to Truschain document and metadata.".to_string(), + ), + content_type: None, + property_set: None, + }; + (res_meta, None, None) + } + Err(ResolverError::MultipleTrustchainProofService) => { + let res_meta = ResolutionMetadata { + error: Some( + "Multiple Trustchain proof service entries are present.".to_string(), + ), + content_type: None, + property_set: None, + }; + (res_meta, None, None) + } + // If not defined error, panic!() + _ => panic!(), + } + } else { + // If doc or doc_meta None, return sidetree resolution as is + (res_meta, None, None) + } + } +} - // TODO: move to Resolver struct. - // let ipfs_client = IpfsClient::default(); - // for endpoint in ipfs_key_endpoints { - // // Download the content of the corresponding CID - // let result = query_ipfs(cid, &ipfs_client).await.unwrap(); +/// Converts DID Document + Metadata to the Trustchain resolved format. +async fn transform_as_result( + res_meta: ResolutionMetadata, + doc: Document, + doc_meta: DocumentMetadata, +) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { + Ok((res_meta, transform_doc(&doc).await, doc_meta)) +} - // // Check the content is a valid public key block. +async fn transform_doc(doc: &Document) -> Document { + // Clone the passed DID document. + let mut doc_clone = doc.clone(); - // // Insert the public key in the list of keys inside the resolved DID document. - // } + // TODO: move ipfs_client to Resolver struct. + let ipfs_client = IpfsClient::default(); - // Duplication? - // // Check if the Trustchain proof service alreday exists in the document. - // let doc_clone = self.remove_proof_service(doc_clone); + let mut verification_methods = match &doc.public_key { + Some(x) => x.clone(), + None => vec!(), + }; + for endpoint in ipfs_key_endpoints(doc) { + // Download the content of the corresponding CID + let ipfs_file = match query_ipfs(endpoint.as_str(), &ipfs_client).await{ + Ok(bytes) => bytes, + Err(_) => todo!(), // see transform method in trustchain-core + }; + let json = match decode_ipfs_content(&ipfs_file){ + Ok(value) => value, + Err(_) => todo!(), // see transform method in trustchain-core + }; - // TODO. - (res_meta, None, doc_meta) + let new_verification_method = match from_str::(&json.to_string()) { + Ok(x) => x, + Err(_) => todo!(), + }; + + verification_methods.push(new_verification_method); } + // Replace the list of verification methods inside the resolved DID document. + doc_clone.public_key = Some(verification_methods.to_owned()); + doc_clone } -// pub trait CASClient { -// fn client() -> Option String> { -// None -// } -// } - -#[async_trait] -impl DIDResolver for Resolver -where - T: DIDResolver + Sync + Send, -{ - async fn resolve( - &self, - did: &str, - input_metadata: &ResolutionInputMetadata, - ) -> ( - ResolutionMetadata, - Option, - Option, - ) { - self.trustchain_resolve(did, input_metadata).await +fn ipfs_key_endpoints(doc: &Document) -> Vec { + let services = &doc.service; + if services.is_none() { + return vec!() } + services.as_ref().unwrap().iter() + .filter(|s| s.type_.to_single().is_some()) + .filter_map(|ref s| { + if s.type_.to_single().as_deref().unwrap().eq(SERVICE_TYPE_IPFS_KEY) { + match s.service_endpoint { + Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()), + _ => None, + } + } else { + None + } + }) + .collect() } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ipfs_key_endpoints() { + + let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap(); + let result = ipfs_key_endpoints(&doc); + + assert_eq!(vec!("QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD"), result); + } + + #[tokio::test] + #[ignore = "Integration test requires IPFS"] + async fn test_transform_doc() { + + let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap(); + let result = transform_doc(&doc).await; + + let expected : Document = serde_json::from_str(TEST_TRANSFORMED_DOCUMENT_IPFS_KEY).unwrap(); + assert_eq!(result, expected); + } + + const TEST_DOCUMENT_IPFS_KEY: &str = r##" + { + "@context" : [ + "https://www.w3.org/ns/did/v1", + { + "@base" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } + ], + "assertionMethod" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "authentication" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "capabilityDelegation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "capabilityInvocation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "id" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", + "keyAgreement" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "service" : [ + { + "id" : "#trustchain-controller-proof", + "type" : "TrustchainProofService", + "serviceEndpoint" : { + "proofValue" : "eyJhbGciOiJFUzI1NksifQ.IkVpQmNiTkRRcjZZNHNzZGc5QXo4eC1qNy1yS1FuNWk5T2Q2S3BjZ2c0RU1KOXci.Nii8p38DtzyurmPHO9sV2JLSH7-Pv-dCKQ0Y-H34rplwhhwca2nSra4ZofcUsHCG6u1oKJ0x4AmMUD2_3UIhRA", + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } + }, + { + "id": "RSSPublicKey", + "type": "IPFSKey", + "serviceEndpoint": "QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD" + } + ], + "verificationMethod" : [ + { + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", + "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84", + "publicKeyJwk" : { + "crv" : "secp256k1", + "kty" : "EC", + "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg", + "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s" + }, + "type" : "JsonWebSignature2020" + } + ] + } + "##; + + const TEST_TRANSFORMED_DOCUMENT_IPFS_KEY: &str = r##" + { + "@context" : [ + "https://www.w3.org/ns/did/v1", + { + "@base" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } + ], + "assertionMethod" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "authentication" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "capabilityDelegation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "capabilityInvocation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "id" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", + "keyAgreement" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" + ], + "service" : [ + { + "id" : "#trustchain-controller-proof", + "type" : "TrustchainProofService", + "serviceEndpoint" : { + "proofValue" : "eyJhbGciOiJFUzI1NksifQ.IkVpQmNiTkRRcjZZNHNzZGc5QXo4eC1qNy1yS1FuNWk5T2Q2S3BjZ2c0RU1KOXci.Nii8p38DtzyurmPHO9sV2JLSH7-Pv-dCKQ0Y-H34rplwhhwca2nSra4ZofcUsHCG6u1oKJ0x4AmMUD2_3UIhRA", + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } + }, + { + "id": "RSSPublicKey", + "type": "IPFSKey", + "serviceEndpoint": "QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD" + } + ], + "verificationMethod" : [ + { + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", + "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84", + "publicKeyJwk" : { + "crv" : "secp256k1", + "kty" : "EC", + "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg", + "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s" + }, + "type" : "JsonWebSignature2020" + }, + { + "id": "YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4", + "type": "JsonWebSignature2020", + "publicKeyJwk": { + "kty": "OKP", + "crv": "RSSKey2023", + "x": "EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4" + }, + "purposes": [ + "assertionMethod", + "authentication", + "keyAgreement", + "capabilityInvocation", + "capabilityDelegation" + ] + } + ] + } + "##; +} \ No newline at end of file From a54e200335d4610ab7131155f5b180d932643419 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Tue, 14 Nov 2023 21:26:56 +0000 Subject: [PATCH 09/17] Add IpfsClient field in Resolver --- trustchain-ion/src/resolver.rs | 37 ++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index ef9a7889..3b7104fc 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -39,7 +39,7 @@ impl DIDResolver for DIDMethodWrapper { /// Trustchain DID document and DID document metadata. pub struct Resolver { pub wrapped_resolver: T, - // pub ipfs_client: IpfsClient, + pub ipfs_client: IpfsClient, } impl Resolver { @@ -47,7 +47,7 @@ impl Resolver { pub fn new(resolver: T) -> Self { Self { wrapped_resolver: resolver, - // ipfs_client: IpfsClient::default(), + ipfs_client: IpfsClient::default() } } /// Constructs a Trustchain resolver from a DIDMethod. @@ -96,10 +96,12 @@ where Option, Option, ) { + // TODO (copied from trustchain-core): + // If a document and document metadata are returned, try to convert if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) { // Convert to trustchain versions - let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta).await; + let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta, &self.ipfs_client).await; match tc_result { // Map the tuple of non-option types to have tuple with optional document // document metadata @@ -142,24 +144,33 @@ async fn transform_as_result( res_meta: ResolutionMetadata, doc: Document, doc_meta: DocumentMetadata, + ipfs_client: &IpfsClient ) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { - Ok((res_meta, transform_doc(&doc).await, doc_meta)) + Ok((res_meta, transform_doc(&doc, ipfs_client).await, doc_meta)) } -async fn transform_doc(doc: &Document) -> Document { +async fn transform_doc(doc: &Document, ipfs_client: &IpfsClient) -> Document { + + // TODO: handle errors throughout: + // Clone the passed DID document. let mut doc_clone = doc.clone(); - // TODO: move ipfs_client to Resolver struct. - let ipfs_client = IpfsClient::default(); + let endpoints = ipfs_key_endpoints(doc); + if endpoints.is_empty() { + return doc_clone + } + // Get the existing verification methods (public keys) in the DID document. let mut verification_methods = match &doc.public_key { Some(x) => x.clone(), None => vec!(), }; - for endpoint in ipfs_key_endpoints(doc) { + + // Add any public keys found on IPFS. + for endpoint in endpoints { // Download the content of the corresponding CID - let ipfs_file = match query_ipfs(endpoint.as_str(), &ipfs_client).await{ + let ipfs_file = match query_ipfs(endpoint.as_str(), ipfs_client).await{ Ok(bytes) => bytes, Err(_) => todo!(), // see transform method in trustchain-core }; @@ -172,10 +183,9 @@ async fn transform_doc(doc: &Document) -> Document { Ok(x) => x, Err(_) => todo!(), }; - verification_methods.push(new_verification_method); } - // Replace the list of verification methods inside the resolved DID document. + // Update the verification methods in the DID document. doc_clone.public_key = Some(verification_methods.to_owned()); doc_clone } @@ -218,10 +228,11 @@ mod tests { async fn test_transform_doc() { let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap(); - let result = transform_doc(&doc).await; + let ipfs_client = IpfsClient::default(); + let result = transform_doc(&doc, &ipfs_client).await; let expected : Document = serde_json::from_str(TEST_TRANSFORMED_DOCUMENT_IPFS_KEY).unwrap(); - assert_eq!(result, expected); + // assert_eq!(result, expected); } const TEST_DOCUMENT_IPFS_KEY: &str = r##" From d85f46fdd8407eec67d032cd931499eabd153236 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Wed, 15 Nov 2023 14:57:44 +0000 Subject: [PATCH 10/17] Finish extended_transform implementation --- trustchain-core/src/resolver.rs | 46 +++++---- trustchain-ion/src/commitment.rs | 2 +- trustchain-ion/src/lib.rs | 7 ++ trustchain-ion/src/resolver.rs | 161 ++++++++++++++++++------------- trustchain-ion/src/utils.rs | 16 ++- trustchain-ion/src/verifier.rs | 4 +- 6 files changed, 143 insertions(+), 93 deletions(-) diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index 250bd3ef..350178ef 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -17,9 +17,9 @@ pub enum ResolverError { /// Controller is already present in DID document. #[error("Controller is already present in DID document.")] ControllerAlreadyPresent, - /// Failed to convert to Truschain document and metadata. - #[error("Failed to convert to Truschain document and metadata.")] - FailedToConvertToTrustchain, + /// Failed to convert to Trustchain document and metadata. + #[error("Failed to convert to Trustchain document and metadata: {0}")] + FailedToConvertToTrustchain(String), /// Multiple Trustchain proof service entries are present. #[error("Multiple Trustchain proof service entries are present.")] MultipleTrustchainProofService, @@ -237,23 +237,19 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { Option, Option, ) { - // Transform - // If a document and document metadata are returned, try to convert + // If a document and document metadata are returned, try to convert. if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) { - // Convert to trustchain versions + // Convert to trustchain versions. let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta); match tc_result { - // Map the tuple of non-option types to have tuple with optional document - // document metadata + // Map the tuple of non-option types to have tuple with optional document metadata Ok((tc_res_meta, tc_doc, tc_doc_meta)) => { (tc_res_meta, Some(tc_doc), Some(tc_doc_meta)) } // If cannot convert, return the relevant error - Err(ResolverError::FailedToConvertToTrustchain) => { + Err(ResolverError::FailedToConvertToTrustchain(err)) => { let res_meta = ResolutionMetadata { - error: Some( - "Failed to convert to Truschain document and metadata.".to_string(), - ), + error: Some(err.to_string()), content_type: None, property_set: None, }; @@ -261,16 +257,20 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { } Err(ResolverError::MultipleTrustchainProofService) => { let res_meta = ResolutionMetadata { - error: Some( - "Multiple Trustchain proof service entries are present.".to_string(), - ), + error: Some("Found multiple Trustchain proof service entries.".to_string()), + content_type: None, + property_set: None, + }; + (res_meta, None, None) + } + Err(err) => { + let res_meta = ResolutionMetadata { + error: Some(err.to_string()), content_type: None, property_set: None, }; (res_meta, None, None) } - // If not defined error, panic!() - _ => panic!(), } } else { // If doc or doc_meta None, return sidetree resolution as is @@ -295,9 +295,15 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { Err(ResolverError::NonExistentDID(did.to_string())) } else if did_res_meta_error == "notFound" { Err(ResolverError::DIDNotFound(did.to_string())) - } else if did_res_meta_error == "Failed to convert to Truschain document and metadata." - { - Err(ResolverError::FailedToConvertToTrustchain) + } else if did_res_meta_error.contains("Failed to convert to Trustchain") { + Err(ResolverError::FailedToConvertToTrustchain( + did_res_meta_error + .to_owned() + .rsplit(":") + .next() + .unwrap_or("") + .to_owned(), + )) } else if did_res_meta_error == "Multiple Trustchain proof service entries are present." { Err(ResolverError::MultipleTrustchainProofService) diff --git a/trustchain-ion/src/commitment.rs b/trustchain-ion/src/commitment.rs index 84d81402..bbc2303f 100644 --- a/trustchain-ion/src/commitment.rs +++ b/trustchain-ion/src/commitment.rs @@ -29,7 +29,7 @@ fn ipfs_hasher() -> fn(&[u8]) -> CommitmentResult { } fn ipfs_decode_candidate_data() -> fn(&[u8]) -> CommitmentResult { - |x| decode_ipfs_content(x).map_err(|_| CommitmentError::DataDecodingFailure) + |x| decode_ipfs_content(x, true).map_err(|_| CommitmentError::DataDecodingFailure) } fn block_header_hasher() -> fn(&[u8]) -> CommitmentResult { diff --git a/trustchain-ion/src/lib.rs b/trustchain-ion/src/lib.rs index c9047ee2..8958aafb 100644 --- a/trustchain-ion/src/lib.rs +++ b/trustchain-ion/src/lib.rs @@ -18,6 +18,7 @@ use crate::ion::IONTest as ION; use crate::resolver::{DIDMethodWrapper, Resolver}; use did_ion::sidetree::SidetreeClient; use serde::{Deserialize, Serialize}; +use std::string::FromUtf8Error; use std::{io, num::ParseIntError}; use thiserror::Error; @@ -109,6 +110,9 @@ pub enum TrustchainIpfsError { #[error("Failed to decode IPFS data.")] DataDecodingError(io::Error), /// Failed to decode IPFS data. + #[error("Failed to decode UTF-8 string.")] + Utf8DecodingError(FromUtf8Error), + /// Failed to decode IPFS data. #[error("Failed to deserialize IPFS content to JSON")] DeserializeError(serde_json::Error), } @@ -145,6 +149,9 @@ pub enum TrustchainBitcoinError { TargetDateOutOfRange, } +// DID +pub const CONTROLLER_KEY: &str = "controller"; + // ION pub const ION_METHOD: &str = "ion"; pub const ION_TEST_METHOD: &str = "ion:test"; diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index 3b7104fc..f1fc8fe9 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,7 +1,6 @@ //! DID resolution and `DIDResolver` implementation. use async_trait::async_trait; use ipfs_api_backend_hyper::IpfsClient; -use serde_json::from_str; use ssi::did::{ServiceEndpoint, VerificationMethod}; use ssi::did_resolve::DocumentMetadata; use ssi::one_or_many::OneOrMany; @@ -9,10 +8,10 @@ use ssi::{ did::{DIDMethod, Document}, did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, }; -use trustchain_core::resolver::{TrustchainResolver, ResolverError}; +use trustchain_core::resolver::{ResolverError, TrustchainResolver}; -use crate::utils::{query_ipfs, decode_ipfs_content}; -use crate::SERVICE_TYPE_IPFS_KEY; +use crate::utils::{decode_ipfs_content, query_ipfs}; +use crate::{CONTROLLER_KEY, SERVICE_TYPE_IPFS_KEY}; // Newtype pattern (workaround for lack of trait upcasting coercion). // Specifically, the DIDMethod method to_resolver() returns a reference but we want ownership. @@ -47,7 +46,7 @@ impl Resolver { pub fn new(resolver: T) -> Self { Self { wrapped_resolver: resolver, - ipfs_client: IpfsClient::default() + ipfs_client: IpfsClient::default(), } } /// Constructs a Trustchain resolver from a DIDMethod. @@ -96,44 +95,28 @@ where Option, Option, ) { - // TODO (copied from trustchain-core): - - // If a document and document metadata are returned, try to convert + // If a document and document metadata are returned, try to convert. if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) { - // Convert to trustchain versions - let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta, &self.ipfs_client).await; + // Convert to trustchain-ion version. + let tc_result = + transform_as_result(res_meta, did_doc, did_doc_meta, &self.ipfs_client).await; match tc_result { - // Map the tuple of non-option types to have tuple with optional document - // document metadata + // Map the tuple of non-option types to have tuple with optional document metadata. Ok((tc_res_meta, tc_doc, tc_doc_meta)) => { (tc_res_meta, Some(tc_doc), Some(tc_doc_meta)) } - // If cannot convert, return the relevant error - Err(ResolverError::FailedToConvertToTrustchain) => { - let res_meta = ResolutionMetadata { - error: Some( - "Failed to convert to Truschain document and metadata.".to_string(), - ), - content_type: None, - property_set: None, - }; - (res_meta, None, None) - } - Err(ResolverError::MultipleTrustchainProofService) => { + // If failed to convert, return the relevant error. + Err(err) => { let res_meta = ResolutionMetadata { - error: Some( - "Multiple Trustchain proof service entries are present.".to_string(), - ), + error: Some(err.to_string()), content_type: None, property_set: None, }; (res_meta, None, None) } - // If not defined error, panic!() - _ => panic!(), } } else { - // If doc or doc_meta None, return sidetree resolution as is + // If doc or doc_meta None, return sidetree resolution as is. (res_meta, None, None) } } @@ -144,61 +127,78 @@ async fn transform_as_result( res_meta: ResolutionMetadata, doc: Document, doc_meta: DocumentMetadata, - ipfs_client: &IpfsClient + ipfs_client: &IpfsClient, ) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> { - Ok((res_meta, transform_doc(&doc, ipfs_client).await, doc_meta)) + Ok((res_meta, transform_doc(&doc, ipfs_client).await?, doc_meta)) } -async fn transform_doc(doc: &Document, ipfs_client: &IpfsClient) -> Document { - - // TODO: handle errors throughout: - +async fn transform_doc( + doc: &Document, + ipfs_client: &IpfsClient, +) -> Result { // Clone the passed DID document. let mut doc_clone = doc.clone(); let endpoints = ipfs_key_endpoints(doc); - if endpoints.is_empty() { - return doc_clone + if endpoints.is_empty() { + return Ok(doc_clone); } // Get the existing verification methods (public keys) in the DID document. let mut verification_methods = match &doc.public_key { Some(x) => x.clone(), - None => vec!(), + None => vec![], }; // Add any public keys found on IPFS. for endpoint in endpoints { // Download the content of the corresponding CID - let ipfs_file = match query_ipfs(endpoint.as_str(), ipfs_client).await{ - Ok(bytes) => bytes, - Err(_) => todo!(), // see transform method in trustchain-core - }; - let json = match decode_ipfs_content(&ipfs_file){ - Ok(value) => value, - Err(_) => todo!(), // see transform method in trustchain-core - }; + let ipfs_file = query_ipfs(endpoint.as_str(), ipfs_client) + .await + .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; + + let mut json = decode_ipfs_content(&ipfs_file, false) + .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; + + // Add the controller in the decoded IPFS content. + json.as_object_mut() + .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( + "VerificationMethodMap missing keys.", + )))? + .insert( + CONTROLLER_KEY.to_owned(), + serde_json::Value::String(doc.id.to_owned()), + ); + + // Can deserialize into untagged enum VerificationMethod from VerificationMethodMap str + let new_verification_method: VerificationMethod = + serde_json::from_str(&json.to_string()) + .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; - let new_verification_method = match from_str::(&json.to_string()) { - Ok(x) => x, - Err(_) => todo!(), - }; verification_methods.push(new_verification_method); } // Update the verification methods in the DID document. doc_clone.public_key = Some(verification_methods.to_owned()); - doc_clone + Ok(doc_clone) } fn ipfs_key_endpoints(doc: &Document) -> Vec { let services = &doc.service; if services.is_none() { - return vec!() + return vec![]; } - services.as_ref().unwrap().iter() + services + .as_ref() + .unwrap() + .iter() .filter(|s| s.type_.to_single().is_some()) .filter_map(|ref s| { - if s.type_.to_single().as_deref().unwrap().eq(SERVICE_TYPE_IPFS_KEY) { + if s.type_ + .to_single() + .as_deref() + .unwrap() + .eq(SERVICE_TYPE_IPFS_KEY) + { match s.service_endpoint { Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()), _ => None, @@ -216,25 +216,56 @@ mod tests { #[test] fn test_ipfs_key_endpoints() { - let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap(); let result = ipfs_key_endpoints(&doc); - - assert_eq!(vec!("QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD"), result); + + assert_eq!( + vec!("QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD"), + result + ); } - + #[tokio::test] #[ignore = "Integration test requires IPFS"] async fn test_transform_doc() { - let doc: Document = serde_json::from_str(TEST_DOCUMENT_IPFS_KEY).unwrap(); let ipfs_client = IpfsClient::default(); - let result = transform_doc(&doc, &ipfs_client).await; + let result = transform_doc(&doc, &ipfs_client).await.unwrap(); + + // Check the IPFS key is in the transformed DID doc. + assert!(result.public_key.unwrap().into_iter().any(|vm| { + match vm { + VerificationMethod::Map(map) => { + map.id.eq("YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4") + } + _ => false, + } + })); + } + + #[test] + fn test_verification_method_deserialisation() { + let mut json: serde_json::Value = serde_json::from_str(&RSS_VM_JSON.to_string()).unwrap(); + + json.as_object_mut() + .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( + "Verification Method Map missing keys.", + ))) + .unwrap() + .insert( + CONTROLLER_KEY.to_owned(), + serde_json::Value::String("did:ion:abc".to_owned()), + ); - let expected : Document = serde_json::from_str(TEST_TRANSFORMED_DOCUMENT_IPFS_KEY).unwrap(); - // assert_eq!(result, expected); + let new_verification_method: ssi::did::VerificationMethod = + serde_json::from_str(&json.to_string()).unwrap(); + // .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; } - + + const RSS_VM_JSON: &str = r##" + {"id":"YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4","type":"JsonWebSignature2020","publicKeyJwk":{"kty":"OKP","crv":"RSSKey2023","x":"EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4"},"purposes":["assertionMethod","authentication","keyAgreement","capabilityInvocation","capabilityDelegation"]} + "##; + const TEST_DOCUMENT_IPFS_KEY: &str = r##" { "@context" : [ @@ -360,4 +391,4 @@ mod tests { ] } "##; -} \ No newline at end of file +} diff --git a/trustchain-ion/src/utils.rs b/trustchain-ion/src/utils.rs index abf1a257..035af198 100644 --- a/trustchain-ion/src/utils.rs +++ b/trustchain-ion/src/utils.rs @@ -83,11 +83,17 @@ pub fn tx_to_op_return_cid(tx: &Transaction) -> Result { } /// Decodes an IPFS file. -pub fn decode_ipfs_content(ipfs_file: &[u8]) -> Result { - // Decompress the content and deserialize to JSON. - let mut decoder = GzDecoder::new(ipfs_file); - let mut ipfs_content_str = String::new(); - decoder.read_to_string(&mut ipfs_content_str)?; +pub fn decode_ipfs_content(ipfs_file: &[u8], gunzip: bool) -> Result { + let mut ipfs_content_str; + if gunzip { + // Decompress the content and deserialize to JSON. + let mut decoder = GzDecoder::new(ipfs_file); + ipfs_content_str = String::new(); + decoder.read_to_string(&mut ipfs_content_str)?; + } else { + ipfs_content_str = String::from_utf8(ipfs_file.to_vec()) + .map_err(|err| TrustchainIpfsError::Utf8DecodingError(err))?; + } Ok(serde_json::from_str(&ipfs_content_str)?) } diff --git a/trustchain-ion/src/verifier.rs b/trustchain-ion/src/verifier.rs index 3474ac4c..36e9d8c1 100644 --- a/trustchain-ion/src/verifier.rs +++ b/trustchain-ion/src/verifier.rs @@ -200,7 +200,7 @@ where &self, core_index_file: &[u8], ) -> Result, VerifierError> { - let content = decode_ipfs_content(core_index_file).map_err(|e| { + let content = decode_ipfs_content(core_index_file, true).map_err(|e| { VerifierError::FailureToFetchVerificationMaterial(format!( "Failed to decode ION core index file: {}", e @@ -222,7 +222,7 @@ where } async fn fetch_chunk_file(&self, prov_index_file: &[u8]) -> Result, VerifierError> { - let content = decode_ipfs_content(prov_index_file).map_err(|err| { + let content = decode_ipfs_content(prov_index_file, true).map_err(|err| { VerifierError::ErrorFetchingVerificationMaterial( "Failed to decode ION provisional index file".to_string(), err.into(), From 26e781fed290c34cd37d68904cf4064434d32fb3 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Wed, 15 Nov 2023 15:36:55 +0000 Subject: [PATCH 11/17] Add getting signing key by thumbprint --- trustchain-core/src/key_manager.rs | 6 +++++- trustchain-ion/src/attestor.rs | 30 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/trustchain-core/src/key_manager.rs b/trustchain-core/src/key_manager.rs index d4232d10..acf5a7c3 100644 --- a/trustchain-core/src/key_manager.rs +++ b/trustchain-core/src/key_manager.rs @@ -9,7 +9,8 @@ use std::path::{Path, PathBuf}; use thiserror::Error; /// An error relating to Trustchain key management. -#[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] +// #[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Error, Debug)] pub enum KeyManagerError { /// Key does not exist. #[error("Key does not exist.")] @@ -35,6 +36,9 @@ pub enum KeyManagerError { /// Expected only one key but found many. #[error("Expected only one key but found many.")] InvalidManyKeys, + /// Wrapped SSI JWK error. + #[error(transparent)] + SSIJWKError(#[from] ssi::jwk::Error), } /// KeyType enum. diff --git a/trustchain-ion/src/attestor.rs b/trustchain-ion/src/attestor.rs index ce420ac8..ab97f61e 100644 --- a/trustchain-ion/src/attestor.rs +++ b/trustchain-ion/src/attestor.rs @@ -52,6 +52,9 @@ impl IONAttestor { return Ok(key_in_loop); } } + if key_in_loop.thumbprint()? == key_id { + return Ok(key_in_loop); + } } // If none of the keys has a matching key_id, the required key does not exist. Err(KeyManagerError::FailedToLoadKey) @@ -435,8 +438,31 @@ mod tests { // With a non-matching key_id, expect KeyManagerError::FailedToLoadKey let actual_key_res = target.signing_key(Some("1")); - let expected_res: Result = Err(KeyManagerError::FailedToLoadKey); - assert_eq!(actual_key_res, expected_res); + assert!(matches!( + actual_key_res, + Err(KeyManagerError::FailedToLoadKey) + )); + Ok(()) + } + + #[test] + fn test_signing_key_with_thumbprint() -> Result<(), Box> { + // Initialize temp path for saving keys + init(); + + // Set-up keys and attestor + let did = "did:example:test_signing_with_thumbrint_key"; + + // Load keys + let keys: Vec = serde_json::from_str(TEST_SIGNING_KEYS)?; + let expected_key = keys.last().unwrap().clone(); + + let target = + IONAttestor::try_from(AttestorData::new(did.to_string(), OneOrMany::Many(keys)))?; + + // With thumbprint passed, expect correct key returned. + let actual_key = target.signing_key(Some(&expected_key.thumbprint().unwrap()))?; + assert_eq!(expected_key, actual_key); Ok(()) } From 8bf2de42a285c4c058f1c5c336edd423231b8b94 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Thu, 16 Nov 2023 09:14:43 +0000 Subject: [PATCH 12/17] Move resolve_did method to FullClient ION verifier --- trustchain-ion/src/verifier.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/trustchain-ion/src/verifier.rs b/trustchain-ion/src/verifier.rs index 36e9d8c1..710f3d25 100644 --- a/trustchain-ion/src/verifier.rs +++ b/trustchain-ion/src/verifier.rs @@ -296,6 +296,18 @@ where } Ok(self.bundles.lock().unwrap().get(did).cloned().unwrap()) } + /// Resolves the given DID to obtain the DID Document and Document Metadata. + async fn resolve_did(&self, did: &str) -> Result<(Document, DocumentMetadata), VerifierError> { + let (res_meta, doc, doc_meta) = self.resolver.resolve_as_result(did).await?; + if let (Some(doc), Some(doc_meta)) = (doc, doc_meta) { + Ok((doc, doc_meta)) + } else { + Err(VerifierError::DIDResolutionError( + format!("Missing Document and/or DocumentMetadata for DID: {}", did), + ResolverError::FailureWithMetadata(res_meta).into(), + )) + } + } } impl IONVerifier where @@ -368,19 +380,6 @@ impl IONVerifier where T: Send + Sync + DIDResolver, { - /// Resolves the given DID to obtain the DID Document and Document Metadata. - async fn resolve_did(&self, did: &str) -> Result<(Document, DocumentMetadata), VerifierError> { - let (res_meta, doc, doc_meta) = self.resolver.resolve_as_result(did).await?; - if let (Some(doc), Some(doc_meta)) = (doc, doc_meta) { - Ok((doc, doc_meta)) - } else { - Err(VerifierError::DIDResolutionError( - format!("Missing Document and/or DocumentMetadata for DID: {}", did), - ResolverError::FailureWithMetadata(res_meta).into(), - )) - } - } - /// Extracts the IPFS content identifier from the ION OP_RETURN data inside a Bitcoin transaction. fn op_return_cid(&self, tx: &Transaction) -> Result { tx_to_op_return_cid(tx) From cc6acaa8da850696debb7b10820759f5430ebfb7 Mon Sep 17 00:00:00 2001 From: Ed Chapman - Turing Date: Thu, 16 Nov 2023 14:41:12 +0000 Subject: [PATCH 13/17] refactor ipfs transform, add key to verification_method --- trustchain-ion/src/resolver.rs | 169 +++++++++++++++++---------------- 1 file changed, 85 insertions(+), 84 deletions(-) diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index f1fc8fe9..d543b0b3 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -1,13 +1,15 @@ //! DID resolution and `DIDResolver` implementation. use async_trait::async_trait; use ipfs_api_backend_hyper::IpfsClient; -use ssi::did::{ServiceEndpoint, VerificationMethod}; +use serde_json::Value; +use ssi::did::{RelativeDIDURL, ServiceEndpoint, VerificationMethod, VerificationMethodMap}; use ssi::did_resolve::DocumentMetadata; use ssi::one_or_many::OneOrMany; use ssi::{ did::{DIDMethod, Document}, did_resolve::{DIDResolver, ResolutionInputMetadata, ResolutionMetadata}, }; +use std::str::FromStr; use trustchain_core::resolver::{ResolverError, TrustchainResolver}; use crate::utils::{decode_ipfs_content, query_ipfs}; @@ -145,7 +147,7 @@ async fn transform_doc( } // Get the existing verification methods (public keys) in the DID document. - let mut verification_methods = match &doc.public_key { + let mut verification_methods = match &doc.verification_method { Some(x) => x.clone(), None => vec![], }; @@ -161,9 +163,10 @@ async fn transform_doc( .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; // Add the controller in the decoded IPFS content. + // TODO: We are only supporting one of the possible ways to express verification methods here. json.as_object_mut() .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( - "VerificationMethodMap missing keys.", + "Unsupported document verification_method, use Vec.", )))? .insert( CONTROLLER_KEY.to_owned(), @@ -171,14 +174,84 @@ async fn transform_doc( ); // Can deserialize into untagged enum VerificationMethod from VerificationMethodMap str - let new_verification_method: VerificationMethod = - serde_json::from_str(&json.to_string()) - .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; + let mut new_vm_map: VerificationMethodMap = serde_json::from_str(&json.to_string()) + .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; + + // Transform public key id into thumbprint format. + if !new_vm_map.id.starts_with("#") { + new_vm_map.id.insert_str(0, "#"); + } + // Create thumbprint verification method + let thumbprint: &str = new_vm_map.id.as_ref(); + let thumbprint_vm = VerificationMethod::RelativeDIDURL( + RelativeDIDURL::from_str(thumbprint) + .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?, + ); + + // Extract the verification method purposes + if let Some(extra_properties) = new_vm_map.property_set.as_mut() { + if let Some(purposes) = extra_properties.remove("purposes") { + let purposes_vec = purposes + .as_array() + .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( + "Expected public key 'purposes' to be a JSON Array.", + )))? + .to_vec(); + + // Propagate public key purposes to associated DID fields. + if purposes_vec.contains(&Value::String("authentication".to_string())) { + if let Some(authentication) = &doc.authentication { + let mut new_authentication = authentication.to_owned(); + new_authentication.push(thumbprint_vm.clone()); + doc_clone.authentication = Some(new_authentication); + } else { + doc_clone.authentication = Some(vec![thumbprint_vm.clone()]) + } + } + if purposes_vec.contains(&Value::String("assertionMethod".to_string())) { + if let Some(assertion_method) = &doc.assertion_method { + let mut new_assertion_method = assertion_method.to_owned(); + new_assertion_method.push(thumbprint_vm.clone()); + doc_clone.assertion_method = Some(new_assertion_method); + } else { + doc_clone.assertion_method = Some(vec![thumbprint_vm.clone()]) + } + } + if purposes_vec.contains(&Value::String("keyAgreement".to_string())) { + if let Some(key_agreement) = &doc.key_agreement { + let mut new_key_agreement = key_agreement.to_owned(); + new_key_agreement.push(thumbprint_vm.clone()); + doc_clone.key_agreement = Some(new_key_agreement); + } else { + doc_clone.key_agreement = Some(vec![thumbprint_vm.clone()]) + } + } + if purposes_vec.contains(&Value::String("capabilityInvocation".to_string())) { + if let Some(capability_invocation) = &doc.capability_invocation { + let mut new_capability_invocation = capability_invocation.to_owned(); + new_capability_invocation.push(thumbprint_vm.clone()); + doc_clone.capability_invocation = Some(new_capability_invocation); + } else { + doc_clone.capability_invocation = Some(vec![thumbprint_vm.clone()]) + } + } + if purposes_vec.contains(&Value::String("capabilityDelegation".to_string())) { + if let Some(capability_delegation) = &doc.capability_delegation { + let mut new_capability_delegation = capability_delegation.to_owned(); + new_capability_delegation.push(thumbprint_vm.clone()); + doc_clone.capability_delegation = Some(new_capability_delegation); + } else { + doc_clone.capability_delegation = Some(vec![thumbprint_vm.clone()]) + } + } + } + } - verification_methods.push(new_verification_method); + verification_methods.push(VerificationMethod::Map(new_vm_map)); } + // Update the verification methods in the DID document. - doc_clone.public_key = Some(verification_methods.to_owned()); + doc_clone.verification_method = Some(verification_methods.to_owned()); Ok(doc_clone) } @@ -232,11 +305,11 @@ mod tests { let ipfs_client = IpfsClient::default(); let result = transform_doc(&doc, &ipfs_client).await.unwrap(); - // Check the IPFS key is in the transformed DID doc. - assert!(result.public_key.unwrap().into_iter().any(|vm| { + // Check the IPFS key is in the transformed DID doc verification methods. + assert!(result.verification_method.unwrap().into_iter().any(|vm| { match vm { VerificationMethod::Map(map) => { - map.id.eq("YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4") + map.id.eq("#YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4") } _ => false, } @@ -257,9 +330,8 @@ mod tests { serde_json::Value::String("did:ion:abc".to_owned()), ); - let new_verification_method: ssi::did::VerificationMethod = + let _new_verification_method: ssi::did::VerificationMethod = serde_json::from_str(&json.to_string()).unwrap(); - // .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; } const RSS_VM_JSON: &str = r##" @@ -320,75 +392,4 @@ mod tests { ] } "##; - - const TEST_TRANSFORMED_DOCUMENT_IPFS_KEY: &str = r##" - { - "@context" : [ - "https://www.w3.org/ns/did/v1", - { - "@base" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" - } - ], - "assertionMethod" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "authentication" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "capabilityDelegation" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "capabilityInvocation" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "id" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", - "keyAgreement" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "service" : [ - { - "id" : "#trustchain-controller-proof", - "type" : "TrustchainProofService", - "serviceEndpoint" : { - "proofValue" : "eyJhbGciOiJFUzI1NksifQ.IkVpQmNiTkRRcjZZNHNzZGc5QXo4eC1qNy1yS1FuNWk5T2Q2S3BjZ2c0RU1KOXci.Nii8p38DtzyurmPHO9sV2JLSH7-Pv-dCKQ0Y-H34rplwhhwca2nSra4ZofcUsHCG6u1oKJ0x4AmMUD2_3UIhRA", - "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" - } - }, - { - "id": "RSSPublicKey", - "type": "IPFSKey", - "serviceEndpoint": "QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD" - } - ], - "verificationMethod" : [ - { - "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", - "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84", - "publicKeyJwk" : { - "crv" : "secp256k1", - "kty" : "EC", - "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg", - "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s" - }, - "type" : "JsonWebSignature2020" - }, - { - "id": "YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4", - "type": "JsonWebSignature2020", - "publicKeyJwk": { - "kty": "OKP", - "crv": "RSSKey2023", - "x": "EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4" - }, - "purposes": [ - "assertionMethod", - "authentication", - "keyAgreement", - "capabilityInvocation", - "capabilityDelegation" - ] - } - ] - } - "##; } From 5ce6d32438bb0f26b378000b2ea85f763de73243 Mon Sep 17 00:00:00 2001 From: Ed Chapman - Turing Date: Thu, 16 Nov 2023 14:54:55 +0000 Subject: [PATCH 14/17] impove variable names to reflect thumbprint convention --- trustchain-ion/src/resolver.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index d543b0b3..6115aaf0 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -177,14 +177,14 @@ async fn transform_doc( let mut new_vm_map: VerificationMethodMap = serde_json::from_str(&json.to_string()) .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; - // Transform public key id into thumbprint format. + // Transform public key id into RelativeDIDURL format. if !new_vm_map.id.starts_with("#") { new_vm_map.id.insert_str(0, "#"); } - // Create thumbprint verification method - let thumbprint: &str = new_vm_map.id.as_ref(); - let thumbprint_vm = VerificationMethod::RelativeDIDURL( - RelativeDIDURL::from_str(thumbprint) + // Create RelativeDIDURL verification method + let relative_did_url: &str = new_vm_map.id.as_ref(); + let relative_did_url_vm = VerificationMethod::RelativeDIDURL( + RelativeDIDURL::from_str(relative_did_url) .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?, ); @@ -202,46 +202,46 @@ async fn transform_doc( if purposes_vec.contains(&Value::String("authentication".to_string())) { if let Some(authentication) = &doc.authentication { let mut new_authentication = authentication.to_owned(); - new_authentication.push(thumbprint_vm.clone()); + new_authentication.push(relative_did_url_vm.clone()); doc_clone.authentication = Some(new_authentication); } else { - doc_clone.authentication = Some(vec![thumbprint_vm.clone()]) + doc_clone.authentication = Some(vec![relative_did_url_vm.clone()]) } } if purposes_vec.contains(&Value::String("assertionMethod".to_string())) { if let Some(assertion_method) = &doc.assertion_method { let mut new_assertion_method = assertion_method.to_owned(); - new_assertion_method.push(thumbprint_vm.clone()); + new_assertion_method.push(relative_did_url_vm.clone()); doc_clone.assertion_method = Some(new_assertion_method); } else { - doc_clone.assertion_method = Some(vec![thumbprint_vm.clone()]) + doc_clone.assertion_method = Some(vec![relative_did_url_vm.clone()]) } } if purposes_vec.contains(&Value::String("keyAgreement".to_string())) { if let Some(key_agreement) = &doc.key_agreement { let mut new_key_agreement = key_agreement.to_owned(); - new_key_agreement.push(thumbprint_vm.clone()); + new_key_agreement.push(relative_did_url_vm.clone()); doc_clone.key_agreement = Some(new_key_agreement); } else { - doc_clone.key_agreement = Some(vec![thumbprint_vm.clone()]) + doc_clone.key_agreement = Some(vec![relative_did_url_vm.clone()]) } } if purposes_vec.contains(&Value::String("capabilityInvocation".to_string())) { if let Some(capability_invocation) = &doc.capability_invocation { let mut new_capability_invocation = capability_invocation.to_owned(); - new_capability_invocation.push(thumbprint_vm.clone()); + new_capability_invocation.push(relative_did_url_vm.clone()); doc_clone.capability_invocation = Some(new_capability_invocation); } else { - doc_clone.capability_invocation = Some(vec![thumbprint_vm.clone()]) + doc_clone.capability_invocation = Some(vec![relative_did_url_vm.clone()]) } } if purposes_vec.contains(&Value::String("capabilityDelegation".to_string())) { if let Some(capability_delegation) = &doc.capability_delegation { let mut new_capability_delegation = capability_delegation.to_owned(); - new_capability_delegation.push(thumbprint_vm.clone()); + new_capability_delegation.push(relative_did_url_vm.clone()); doc_clone.capability_delegation = Some(new_capability_delegation); } else { - doc_clone.capability_delegation = Some(vec![thumbprint_vm.clone()]) + doc_clone.capability_delegation = Some(vec![relative_did_url_vm.clone()]) } } } From 2223c204e56d84f27aa9976bd2fefd9b12d4f8f0 Mon Sep 17 00:00:00 2001 From: Ed Chapman <93717706+edchapman88@users.noreply.github.com> Date: Fri, 17 Nov 2023 11:59:27 +0000 Subject: [PATCH 15/17] Apply suggestions from code review pr small suggestion fixes --- trustchain-core/src/key_manager.rs | 1 - trustchain-core/src/resolver.rs | 10 +--------- trustchain-ion/Cargo.toml | 1 - 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/trustchain-core/src/key_manager.rs b/trustchain-core/src/key_manager.rs index acf5a7c3..3eec73a4 100644 --- a/trustchain-core/src/key_manager.rs +++ b/trustchain-core/src/key_manager.rs @@ -9,7 +9,6 @@ use std::path::{Path, PathBuf}; use thiserror::Error; /// An error relating to Trustchain key management. -// #[derive(Error, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Error, Debug)] pub enum KeyManagerError { /// Key does not exist. diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index 350178ef..e9913de4 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -110,8 +110,6 @@ fn get_from_proof_service<'a>(proof_service: &'a Service, key: &str) -> Option<& /// Adds a proof from a DID Document to DocumentMetadata. fn add_proof(doc: &Document, mut doc_meta: DocumentMetadata) -> DocumentMetadata { - // Check if the Trustchain proof service exists in document - // Get proof service let proof_service = get_proof_service(doc); @@ -165,9 +163,6 @@ fn transform_doc(doc: &Document, controller_did: &str) -> Document { // Clone the passed DID document. let doc_clone = doc.clone(); - // Duplication? - // // Check if the Trustchain proof service alreday exists in the document. - // let doc_clone = self.remove_proof_service(doc_clone); // Add controller let doc_clone = @@ -212,7 +207,7 @@ fn transform_as_result( // Return tuple Ok((res_meta, doc, doc_meta)) } else { - // TODO: If proof service is not present or multiple, just return Ok for now. + // If proof service is not present, return Ok. Ok((sidetree_res_meta, sidetree_doc, sidetree_doc_meta)) } } @@ -338,8 +333,6 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { .resolve(did, &ResolutionInputMetadata::default()) .await; } - // let ion_resolver = self.wrapped_resolver; - // let resolved = ion_resolver.resolve(did, input_metadata).await; let resolved = self.wrapped_resolver().resolve(did, input_metadata).await; @@ -436,7 +429,6 @@ mod tests { assert!(did_doc.controller.is_some()); // Construct a Resolver instance. - // let resolver = Resolver::new(get_http_resolver()); // Attempt to add the controller. let result = add_controller(did_doc, controller_did); diff --git a/trustchain-ion/Cargo.toml b/trustchain-ion/Cargo.toml index 28d1150b..03859bcd 100644 --- a/trustchain-ion/Cargo.toml +++ b/trustchain-ion/Cargo.toml @@ -16,7 +16,6 @@ bitcoincore-rpc = "0.16.0" canonical_json = "0.4.0" chrono = "0.4" clap = { version = "^4.1", features=["derive", "cargo"] } -did-method-key = {git="https://github.com/alan-turing-institute/ssi.git", branch="modify-encode-sign-jwt"} did-ion = {git="https://github.com/alan-turing-institute/ssi.git", branch="modify-encode-sign-jwt"} ed25519-dalek-bip32 = "0.3.0" flate2 = "1.0.24" From e2e8f3471f8afba4d3d27c092fd01f67d58f8f60 Mon Sep 17 00:00:00 2001 From: Sam Greenbury Date: Fri, 17 Nov 2023 12:17:35 +0000 Subject: [PATCH 16/17] Clippy --- trustchain-core/src/resolver.rs | 3 +-- trustchain-ion/src/attestor.rs | 2 +- trustchain-ion/src/resolver.rs | 19 +++++++------------ trustchain-ion/src/utils.rs | 18 ++++++++++-------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/trustchain-core/src/resolver.rs b/trustchain-core/src/resolver.rs index e9913de4..97a8c6e8 100644 --- a/trustchain-core/src/resolver.rs +++ b/trustchain-core/src/resolver.rs @@ -163,7 +163,6 @@ fn transform_doc(doc: &Document, controller_did: &str) -> Document { // Clone the passed DID document. let doc_clone = doc.clone(); - // Add controller let doc_clone = add_controller(doc_clone, controller_did).expect("Controller already present in document."); @@ -294,7 +293,7 @@ pub trait TrustchainResolver: DIDResolver + AsDIDResolver { Err(ResolverError::FailedToConvertToTrustchain( did_res_meta_error .to_owned() - .rsplit(":") + .rsplit(':') .next() .unwrap_or("") .to_owned(), diff --git a/trustchain-ion/src/attestor.rs b/trustchain-ion/src/attestor.rs index ab97f61e..e19ff260 100644 --- a/trustchain-ion/src/attestor.rs +++ b/trustchain-ion/src/attestor.rs @@ -163,7 +163,7 @@ impl Issuer for IONAttestor { let proof = credential .generate_proof( &signing_key, - &linked_data_proof_options.unwrap_or(LinkedDataProofOptions::default()), + &linked_data_proof_options.unwrap_or_default(), resolver.as_did_resolver(), context_loader, ) diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index 6115aaf0..da7c2ea2 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -178,8 +178,8 @@ async fn transform_doc( .map_err(|err| ResolverError::FailedToConvertToTrustchain(err.to_string()))?; // Transform public key id into RelativeDIDURL format. - if !new_vm_map.id.starts_with("#") { - new_vm_map.id.insert_str(0, "#"); + if !new_vm_map.id.starts_with('#') { + new_vm_map.id.insert(0, '#'); } // Create RelativeDIDURL verification method let relative_did_url: &str = new_vm_map.id.as_ref(); @@ -265,13 +265,8 @@ fn ipfs_key_endpoints(doc: &Document) -> Vec { .unwrap() .iter() .filter(|s| s.type_.to_single().is_some()) - .filter_map(|ref s| { - if s.type_ - .to_single() - .as_deref() - .unwrap() - .eq(SERVICE_TYPE_IPFS_KEY) - { + .filter_map(|s| { + if s.type_.to_single().unwrap().eq(SERVICE_TYPE_IPFS_KEY) { match s.service_endpoint { Some(OneOrMany::One(ServiceEndpoint::URI(ref uri))) => Some(uri.to_owned()), _ => None, @@ -318,7 +313,7 @@ mod tests { #[test] fn test_verification_method_deserialisation() { - let mut json: serde_json::Value = serde_json::from_str(&RSS_VM_JSON.to_string()).unwrap(); + let mut json: serde_json::Value = serde_json::from_str(RSS_VM_JSON).unwrap(); json.as_object_mut() .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( @@ -334,9 +329,9 @@ mod tests { serde_json::from_str(&json.to_string()).unwrap(); } - const RSS_VM_JSON: &str = r##" + const RSS_VM_JSON: &str = r#" {"id":"YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4","type":"JsonWebSignature2020","publicKeyJwk":{"kty":"OKP","crv":"RSSKey2023","x":"EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4"},"purposes":["assertionMethod","authentication","keyAgreement","capabilityInvocation","capabilityDelegation"]} - "##; + "#; const TEST_DOCUMENT_IPFS_KEY: &str = r##" { diff --git a/trustchain-ion/src/utils.rs b/trustchain-ion/src/utils.rs index 035af198..ead01dfd 100644 --- a/trustchain-ion/src/utils.rs +++ b/trustchain-ion/src/utils.rs @@ -10,8 +10,8 @@ use futures::TryStreamExt; use ipfs_api_backend_hyper::{IpfsApi, IpfsClient}; use mongodb::{bson::doc, options::ClientOptions, Cursor}; use serde_json::{json, Value}; -use std::collections::HashMap; use std::io::Read; +use std::{cmp::Ordering, collections::HashMap}; use trustchain_core::{utils::get_did_suffix, verifier::VerifierError}; use crate::{ @@ -92,7 +92,7 @@ pub fn decode_ipfs_content(ipfs_file: &[u8], gunzip: bool) -> Result target_unixtime { - end_height = current_height; // TODO CHECK: original script has: current_height - 1; - } else if (current_unixtime as i64) < target_unixtime { - start_height = current_height; // TODO CHECK: original script has: current_height + 1; + match (current_unixtime as i64).cmp(&target_unixtime) { + // TODO CHECK: original script has: current_height - 1 + Ordering::Greater => end_height = current_height, + // TODO CHECK: original script has: current_height + 1; + Ordering::Less => start_height = current_height, + // TODO: WHAT IF current_unixtime == target_unixtime? + // (does the loop exit and is start_height the right result in that case?) + Ordering::Equal => unimplemented!(), } - // TODO: WHAT IF current_unixtime == target_unixtime? - // (does the loop exit and is start_height the right result in that case?) } Ok(start_height) } From 2da3ae8ba214dc8ed9066de6cd9c51020c723471 Mon Sep 17 00:00:00 2001 From: Tim Hobson Date: Fri, 17 Nov 2023 13:58:02 +0000 Subject: [PATCH 17/17] Move test fixtures to data.rs --- trustchain-ion/src/data.rs | 59 +++++++++++++++++++++++++++++++ trustchain-ion/src/resolver.rs | 64 +++------------------------------- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/trustchain-ion/src/data.rs b/trustchain-ion/src/data.rs index b13728f0..ce3f29c9 100644 --- a/trustchain-ion/src/data.rs +++ b/trustchain-ion/src/data.rs @@ -109,3 +109,62 @@ pub(crate) const TEST_TRANSACTION_HEX: &str = "020000000171dd04bd101ae70230e01c5 pub(crate) const TEST_MERKLE_BLOCK_HEX: &str = "00e0e42c325b885a35b8655986db88288f0264d4f67f5cc90e6d0d11270000000000000069ad9c5211416544200698706877c62e7cc93a29f5f5a31d05b5d4095279ce7d3d315163c0ff3f1971ea2df61d0000000603d3ca69a3614acb45a14966c812cd9ee034c705f20fac3daf8f796c99f4d805a5fd8e761ae2eb9e0b0e4d62d19599586fb98e8a7be6fc7113441e556fb31ff82c9cea8457c7c57e41f2eaf32ea66177c50be3c24053444234920d95ca3cc49d00a31f6e6d1864017f9cf9d48b51274871c4700e7091dfef14af9c92c5340215b7d88cc8202188e3837b171dba14ffede8f145b2c87c1dbc3642669930517958fb75429c45acaa51c416b283604d515f80f95ddb4f610e8ddb7876985713877602af00"; pub(crate) const TEST_BLOCK_HEADER_HEX: &str = "00e0e42c325b885a35b8655986db88288f0264d4f67f5cc90e6d0d11270000000000000069ad9c5211416544200698706877c62e7cc93a29f5f5a31d05b5d4095279ce7d3d315163c0ff3f1971ea2df6"; + +pub(crate) const TEST_DOCUMENT_IPFS_KEY: &str = r##" +{ +"@context" : [ + "https://www.w3.org/ns/did/v1", + { + "@base" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } +], +"assertionMethod" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" +], +"authentication" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" +], +"capabilityDelegation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" +], +"capabilityInvocation" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" +], +"id" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", +"keyAgreement" : [ + "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" +], +"service" : [ + { + "id" : "#trustchain-controller-proof", + "type" : "TrustchainProofService", + "serviceEndpoint" : { + "proofValue" : "eyJhbGciOiJFUzI1NksifQ.IkVpQmNiTkRRcjZZNHNzZGc5QXo4eC1qNy1yS1FuNWk5T2Q2S3BjZ2c0RU1KOXci.Nii8p38DtzyurmPHO9sV2JLSH7-Pv-dCKQ0Y-H34rplwhhwca2nSra4ZofcUsHCG6u1oKJ0x4AmMUD2_3UIhRA", + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" + } + }, + { + "id": "RSSPublicKey", + "type": "IPFSKey", + "serviceEndpoint": "QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD" + } +], +"verificationMethod" : [ + { + "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", + "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84", + "publicKeyJwk" : { + "crv" : "secp256k1", + "kty" : "EC", + "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg", + "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s" + }, + "type" : "JsonWebSignature2020" + } +] +} +"##; + +pub(crate) const TEST_RSS_VM_JSON: &str = r#" +{"id":"YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4","type":"JsonWebSignature2020","publicKeyJwk":{"kty":"OKP","crv":"RSSKey2023","x":"EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4"},"purposes":["assertionMethod","authentication","keyAgreement","capabilityInvocation","capabilityDelegation"]} +"#; diff --git a/trustchain-ion/src/resolver.rs b/trustchain-ion/src/resolver.rs index da7c2ea2..0a2fd46e 100644 --- a/trustchain-ion/src/resolver.rs +++ b/trustchain-ion/src/resolver.rs @@ -198,6 +198,7 @@ async fn transform_doc( )))? .to_vec(); + // TODO: consider separate function to avoid repetition here. // Propagate public key purposes to associated DID fields. if purposes_vec.contains(&Value::String("authentication".to_string())) { if let Some(authentication) = &doc.authentication { @@ -280,6 +281,8 @@ fn ipfs_key_endpoints(doc: &Document) -> Vec { #[cfg(test)] mod tests { + use crate::data::{TEST_DOCUMENT_IPFS_KEY, TEST_RSS_VM_JSON}; + use super::*; #[test] @@ -313,7 +316,7 @@ mod tests { #[test] fn test_verification_method_deserialisation() { - let mut json: serde_json::Value = serde_json::from_str(RSS_VM_JSON).unwrap(); + let mut json: serde_json::Value = serde_json::from_str(TEST_RSS_VM_JSON).unwrap(); json.as_object_mut() .ok_or(ResolverError::FailedToConvertToTrustchain(String::from( @@ -328,63 +331,4 @@ mod tests { let _new_verification_method: ssi::did::VerificationMethod = serde_json::from_str(&json.to_string()).unwrap(); } - - const RSS_VM_JSON: &str = r#" - {"id":"YGmbDaADvTGg3wopszo23Uqcgr3rNQY6njibaO9_QF4","type":"JsonWebSignature2020","publicKeyJwk":{"kty":"OKP","crv":"RSSKey2023","x":"EyGvw3AkcUf2TZToBh6pddeaaocmvTuLCSLun_yYJpL7x0W3gVEzeKlj06J5Sej9Duk0W_yGhbOKCahOx16LszwTHVgnH9FjRk0nwOer4yKaKnjTZ2FlZsYI0OI__jhCGP9cbcOEd-1rfvUFu-ghsj6oHfSXDBm0Ekplkgs1IktoicuMsF-bD7I6tZRpP9tqFGqARUqvR2daQN-scwYUNsv5ap3XakBCDvOCBc_rPAwzapY_nuC3L6x60UGBAPtUBANdaMhAU0gxd-3JMjcSjFgwzAhw5Eorr7bIp1_od6OfBRYu3sIkij5Es6RDBLghUAx2Z3dznniJRh5Xlx_8zn4SYw_xhV1X04vY5U4O7-7veKMqKxzzoGOR7O137gSTtAjdkWm_q35_KBo-SuO9RrHI8J91pJ4cJktXxMm2yhO1UnmzrQ6hu9YiKeI1kOsq2QJfLlCebKkvOI_KHmx3hUIu1wfEPCp8R7TWeP0LV3hjo0fpTg4fK9hizfCy5agdog6piegS_MB9Myka0DAInA-_YyRXUF1YhXW-Olza-Bk7-33xpfWiQK-78IN8VgcQ8AZ0eVn0L9s2hOpUXCmMmlZT4OQ9uryBJ17HMpR-EbxaqYlmMj7H2toZWjeNOprsexP9S2ZP0fqJbno2oLdhLLW3KyP4UzJltdR0IpsMDpT7nf05HxpJxNKCwMCASuVYMhAK0mZiL-3IjYO2Xa8N0oQxMwq3UuAgcQqSoqrk-CukR3JzS4lQk6LUUrH9Sej47RndsdAqjitadwznsTvxCHSNrWEjjh7aKxHW03jtGwfIZCwROF7mglHdhuzHYTE9Pw7S_fOcXfTglQbs6iz5OEVqyvUMcz8LPfK1SC-H3160XkL_8_4hxMo4ftKRkRRMmBZ-xDTbhCMtpJ5hEy85hD3LP2hPwuCPS2mOOGuaLXDm8CkNe_g52568yQM978Bv6BULHgYtl__wxn83Yvks1wNyTozFZJAV3mWXxS6vg8aiqcWBS5-bvEBsNu0PIzrHVQfAPz19e2kDSM2D59naZzg5Cyl7AuUYpYX_Ad9pt_Ro-wuXsiw7TTolCoyhgj7n6QEnESZ2zflXCDCYK09HDUnD9nFkBli8-DNqBRuzY2FGH3MknjdUCaREEziBuVhHQxfb-beH_VOxRSEHguz6JkMM8nB7myCz2dEQzr2KhdDvfsjtbso_mniq9_Ag4RxBZwkGxUWReivSLqI6AuufQXoK2FBRMoIuiadv1qYlzVM6lqWN0RXAFRtV9B3Z5bCLDwLW2ZcDobmXkk62STpkPaVUNVq8BwrRxGyuvmCPrX5e2Se5AxYIegOwd-Nbo3Xa8gy3jod3B9NCTiiqrgwl4RtYxcmh7RAydl7YV200RK1QRnRCdZwGYOHoTQrtEZTO0gswCgVCEyvg1RdhQlILYgRs_A3woHeUOaXeEdfUK_DvXM9TW8vegoZCZXDjYjmwcRBwrfZs1nDuUajev02fJPioJhoSaZH387XO93J3O4KmfvLOE4mgryrIAAyVJU0eohdBMbmLblvTAH1_Hdv_usK6XkOpEttkxII_nHGL0SHNHeJrMpltGaAFIfZaHX15OgbWsF53fe7Ds20NvuKSQCbUv62bYE6aiHWtsZX1L4n1b0UIN2QOr6VE3MdYSCD_twoIceEiWDH9-JxzDwJkHR4QwYbnzlVNggEKmjoS8gjrlr4se2gQvpn736EnDC7S5hnEoy09VqQ2H_xkEAVmNbExw4E0Fk56hByZQSSMDpDo0vcqSCYGSESlK0KkQUg31Qkf3TbUSjXF-rn73o9IgfMhzAKA3GAWHoi-ruQlgNMxN0UhSgelL3Qj5E1sv_EuIwgTap0SRBni2n9tCcCXVy3cnGeGgOQjdIhlta-o5g0smoY4t06sw566Mv3LjMxy1gD4QivJxPuyeifnIZqBkDm2SyAGeg3I_sVB2PaIAPyIx5154cE92ESLfKIBuiJ_9whJYCjSwfWq4sU89GiiAbbUdaWmQKrVy3GIGZ8sWMeAdg5CNWOqZb6TazY7KggXgwpbm0oyXSljjyLqcgxDk2CiNvWhQhrYC9NdWo1ZipuUsx7uEuQxaVpFLG-2gG1I7xZAL5n9mTIoLopK9BA_VM7et4QSmSrUbeA_adabCOY_4V9dxo1hH7aRQr43O0q2OBJhz9cBqaDNwmDzkjHbx73ja2QR90A1QGo1f98RMJ58nxb0lZfr4Kox3QVSKnrIvxhiNy8VwNBI8_P4AE_N6BX6uB_dAgNjBL5nzgu5m-PivAHAn8jSAK2Esbkf5UafTwPOUuO2dZtFYfq_lMDPomqqoF4idKpaD3qH1s_lRM1p0sHRdjjt1oG15CAUcaJTFBHwSOIsa8tzVYn-MrBOseZl6HtDVYp0CLhwsEUSiokH4mtfpbCaCm-ZAz4yGBRyUBHhAFeZFS2iNrkOo-wtmlWoryUPI5tFuOKZkm2oCsGdOARRYp0WVBKajVunZdgnmQJzww8kVbAvK0mpMcmeYyRBfevFkjGLWbye8XeN-jwnZ5ljuk8Ix9Jonr4PIrQJnwVQtU2DLjm5A8w1NdynibZHmNMd6gRd2dIzAWrlBmVotw3GXqsKM40azVPkZ4qxIQnKXQEVNJ0zgMRZrcBgfGp4ZFH2zXf1AqUV0noDSfPdkb3NK2rWy6h2VxbhgOGKkRJDR221ixdrSWuHYtrjt2vFSF5Z52sFusDer0YkVeq8mB9DW9rmrRNK4E1LSskpLQT1w8b78vXdo3V69oSxHgo1hGIphFIf9E7Ab5e99mY9KX6ixqLS8P8OO1zpm9ofzxSouDzHMraMIc6Gx7EtExRzWFyzmT6JdKMzTJVAobQHMvIuFGv-s3JjORXvaXbixAl-EVopmiCTu5-HBsDJhXSi1rnM4DuwihrXOu6cD9mAi97oDSWCvFQYSOp9gKnNxAJO8sEy8ibgJ5BNj6KTt-H6aa5x2TFTXdjPEuwKMVJ9YPLQPofx07rcRXLMr7_BHATwPmuIkBCAfoOeNmW-VsFve1Ev8PW9P-C5wEZ0lMnKjws9fLUbDkXF3kfPGbY3Sw6icPfAuZVeMffzon2tRBzbf62vTd6qfwnE8Oh0__xRXqmXkjNcqwQlw6PwA6Azlg1OwSww6EGXRTkmfGdP5WE8ghfZvWe3rkY0Z2QGH_Rk647pOhK0YJ9TAcqcPzkL2g64e-9VQdnRUfpo-E8RLDpjrdp4mtOPeE39Yzjv27uJcHghI6dzffGAB-SeYcHmtwJpx9rykn2-14JHYPBeFf4okRdHZeOmgV1qvk_wrkRvnYeB8oS9y_P7K5Nl2izkxBncB-UOO16zvqeplUxDe2AD-iXEaMUVrPKDPmBXUW8cJdFEd7pFu00UddZoER0XyAIP3XCw3wZuhtlXvwJBz5DIMXhmL2u-zI-uL13VTW043jlqPrQptQCzZdZgK9DOeAutUfsq_lA_wtFXcfjr4H7GwbnTL92WcB4ulzWboEFeh1hVDgp1cAcdqPztpCMCXBOM_gb4zNtMi21I4vLd_RpTcwCBQRD2YBHEPlCNiUhIx59xmO6U1rJ5J_ym1vQEbgwAOnAnLWJfmTqJAzACrR3OYiG0Lx7_wcqVONiU6ReWPjHkZW-iDLZXoomBbTxhfg0zD6TnKRhv33PTmc8ZWZF57tCG4jNR-Y7MNyTErtFGScKgW1oAZvQbF7UzDMm3GAV7EYOkF_AB0jNVNZ_UtoFDpSHkNc_rVIKz12hp_xZl0_TC1ujk7v9GU7SFWxRLTBbMVyCN_nvLPbFMlbSDb_xpBIm5w0U-k511pNPno3SvPLe0vViAXSTDmvdok5FbQQ7jIEl8tOTVEI1cTnj0WAAo-GfuUWrRiL8UzrNXX13I7Iy5CryhUTq75csDs_m_oQua7vQdTdihdSqxoXuPykB7d-oh-LKvezLmeI-2BKpkgA47IoD01_HUa6RojeULFAT-vt1eD_-BT6_K4H56iNRYrxq3huHpzG5hxseoG_-CNKdaTF9nTZ0rtu8J8z4jWQqpseCnfFNgojgjU7qxzobD4046t0IjrU3l2Oe1xoTk0oTt_K_6ppY1wpXOeSUrHAWi5tD3QMjuK7CVvZ-6qsaZjIorqsZlWP14difoQujNtJ-0dWxs0lB88F13o3T3kReZ5wheIuLH_s24sgr2KKn8zAUAf6FYasQJXo0ZIqrvLqfMs8Q2nq-zoz6HFsiJxgNsEYIiUtz0X4RSRCQxpen6LSpamF1a8uKGrgXeVZKops0YyCDor_3IIR8eTRbnlAFE-CdarlA4KW-7xdlDp4zOfyGs5NZFMCX1CVUt38STXcvqAkOzjoTN1TQmO2AFzAKonHLo8DF_ZgdDE83i-1pQjfJ3rCgF6FLz74t5IRozJMIL_olUwoOwzFd24R7xxRMfAPT9kMFwB0EmqR_CmIuHNIuH0V7BgkVCV5AaaTOXm5XRK_Gs-14_AkO-kK9jugzqtWsZc7A6XoG3X6Wca3BKNYY_PZZfsJKL2Ttb-qzGRC5P4dBlexvyf9VlxiZqfgWe2i-gfd0Zdb0trUcklxrAZ84HVaXgxifHJ6A2XYb_4SiBtbR5AowOpfBd4dBsWgeY1VbJNk_1rdONv2et7NSTGsPnnL7b1s0Rwwcn4BG3k-YmPPOpiRluKSVGVOeYuRYi57JEBYgkT4Ndq1EzCJsy43AEmpfQW3rPw_7NxFDYOP-_gsISPCma1rvN6M4kkhaBY-TO748qd0gQnYPPnOVhab07thWR6ENQaF0ZTfd9chNlweVqYyj1hM5rKxaXIhahCpz0XHJsmtOiKrqkHSuyxfm8zjD3ZX7ov1wsoo9cRE88vedbNHEsb9JJfOay7gT0Su2sIwWeTep_3LjmMDKNzWtfV3dCH0QuBqSU_hHilryRg3XNsT8SeSP6YBPjehHf8MDNR-_cCaayEWW5hlkiSaWrOqNjvDXwHtyTSlh6dD4MGbJQw-WFcLfdVZ4qBRFUFJdblau-B4JWioWFM0DC6zNrVMXrKzMCfQVT_s6O5KF5cp7rH9mez-sqbW1W-QqPUTs0LcbD3rnDDch6EuamnVHDPzOgsT04VnOzuvMBhkXWIS96hzHcsyZMUDewTxef2QJoNl4TJs3tjJskxTbYIhwNbD5zvYgrGTarnI1EvuBZqXvgvrWYfIXYDUsiMxWiPMeFeMLFPO1eOdha5pVuRmQbhH2UdFcwHKgzwtUbD1YkVf1yxzb1SRkWs2G4pAIFDsQ0Ag88h_aMchsJ6Kz2bgtUTwwklyzbnR-jp5RINaiKCfh2zoDrKNC9elKUSBLOpk4Z8l4-lwfm1dYlM_I5h9nwwZAZXcn6LNE4ee6XNY3A8JJy2y180iGyHyXhPgkSUmQzFvPQH8Tf6N0oEHPfOgoCpMdx7gH4MX91rT3axkZBdQCvOl1cD5iKF05lHgbMX30bwMwI5mipnu-EvuBnlE67O7O_O-xJNw7HpuGbXKRXu8eipzkLK0B81jwuVAJYeMnHNprXMUw3zk_DPZ3hZ-ieg7GyPwdbM3AuKgcJwAtRPYS8YGE86OH-EHVAZQwp-8SNcNKFPqsIV6WGzd7Rri0_KRA0yDUNPq5vwuaAelefzeqJa-ax-MPzH5mBiQWSGTRjT8OUU-KkP3nyRi4wLEeqbPfTwxPgECJeiPx25JHngqWtpa5RBrRk5FEkGxcsXWPNHG6pZvRZGJh1Jl8C80LiOS-JeXZzCNp9WYRkuNrLTeC1YPJzYFdxMMRZHgqRfUxiwHv2eBfM88n8MapLuRt-p_1_Dgbg4x5SRJEjo1ePLwQ_liGwXKUaOKgMswYvQlVPs6CSpmGzC8ZLix1w281LhSAjYBTBG0TLmzlyaFDiNDH_X9KVW4AdNVLodAOa4TbvXT1OQzH5BmsNPCEdOczYCIdKjEyQcRsV2F6U4A-3asdTTvCbxckRBoeP2w2CFop470c01mUT6Dnx3d3CIu3B5PeDbEl37tBrLx46mw5M_TTCZeNOSfPd5bDodg9B6B3mQdzRNzW-w0ZqIWIHs4XCsfzJ-m1HajS_Eo9mgBG_PcykUNfrAJ67hI_KPBHGq1F8Ef6-wdDnyNm0frIXFds79ileVsV_BHaItzFTf2LcG-Ye6-TqsCcr7UI6ShVwOviVaF_phaA-5qqumhWfNB5eWeoN2SxX_IZDtqDT42_YeJeR2P1cvj1iSIBk5A6W_Fi_7t_YiCPnvlFSCIDpW9WX4nxPtIqT-G1GTRPFBC35Deila5ARY6MdT2THwxnwL_HNNZvTEcPgSLcKjenpnxuyWffYNhtUDjE58bc_NwziOdFJPQJjXe3GyicinxQYJjnx1nB9O-_M2Y4Nj0u2kz6lnnyGqJTlX0aA72q4YNXXGxKeWI-rtX0lSV7cZw7PYByJicLeWXi_LEePFr3wlaLpJplHTf62pyXXn3XFJVB17tIEDoMYMcHeFIdJ8wjt0RWHiNkBMusSzpptoTD2duxjE9jC71FlBPNIjtxmcsquPHaNaVMVlsH8aIXXgxiLS94GA2j7P-7Q7Af9gXIXwTETGngv4_n3W3K0wVzuh9O-wItAc4KsCR4kP8dt-g0kOmUpt7gr_JiAwjd3arImJc9xFlhOB3tOoVULj-yaTa0fwT63zqPRJ288sHFL_AdQlSUj8KtcDBbyVyopXgSNTMxczcxpyB8fU6z0SgWnxXPiGEgeyYdOt5MKGLNxBAf0c1sCaW4Fa-UK_BBjmrupMvXBVYvXMzW8i-gx31D3ziA15GZOAdXeNEQdmxBGoRX9mgh5d0IjGA0iuFX0mnZfR8z0LSkH9pF5-F96CENxmXU3j_AcPM2ZlxhtdOAKuQ18knQWgxTBtR-RAqX9AOzHHnltlgn998tVl9qII9GY6VuudYe_J0jiNLSuRImS_wkNp1a6s6ZfQ0jOuJgv2M6Ip1Kj_MfLTD0YL4S8hvgfFR2UdMt9ddN5tTqV4NBdlREJftBii86VflrQ5Ec7KG6_EGfPRmD7J5MfZuS461yrNShIB6YiISTpef9gbvkXagIzSlZn8dc8QKN2Ltcih8bb9JfYUDAr4ndWJrUc58Y--S6YPl9R6xv62n-XE0FVShPkJI-LAAKFqT-UtdIwPcVb2qfqWytLPUGrrqMrOZZL-AN4zM4tWgObxuld78Ql7BaMnEZ-f1NYHzKExys6XdIX0bqTuyeNXruzTvnbis1fnzV41j9rqAxAcP3kvLhmkA7QmH28-Tdhaog3fk83eA--S2ef1MFq1P8-xrb3t73YaUWxYaDrRuJ_kntaNDafTRmsqWI7ptDssFOqzvmPgWgQM6PY_cDOnoJdedIZ3NMfrePJIDQxWF94nzJJAnaGbRCSJ0AJpfhYyZF8fTT8OB-kgh_mmNf46Mem1V_unkkUL-hRWL0HYXxWmD-ztDEZThiEBq5JmT22fOK8OrF7HtfQNBJxRkmzV1ZJCN9oZntgKRh_N1N6-PmDUXH-qiKmGQ82hNNq7K9_-ooG4TTOgLnrKrlpPARI7MAOeCnBUkrpL61IDN4Ktaet6LpZwlktRgF5WEhAS6WjJx8jk0xL6OBLeggTvJieB8YlOqO4r6it3CsEwj0IqbniaooJosTd1wRknHq5cFKG4IAwjMsSv8f1UTIFhwCa2wID_pcRpWA6ujjPg97AZk2QwxAO8zQJ3wkSukzsbgOWC_jIWM2Lf8P9YXkgCAh9-tlpP3yRnpDG1t4efBQE4LK9Fxy3sa_FXxnHFagTENdQ6fG05Ao89lNJG3fvNUwCDjeNmWCy-IYfyblVQnYAZgCi-6hB7FlDfAC9PbkgX9RqA3q8dGfnn5h52VQ-Ts90X-B2Rqh_lOQAy8haZrzBCY-Spvvw1NMg_hBe49MTATla_OPh4rsRtSkIj2JR3D7OOYQALwIshjCquOJOxk4_S4X9rcVuQjbApwmk15wlibnWLIwrLuFJw_q7hKywfcznPJqsolADHqMcDWi0vR9Fd_1cQxgWicacfQlwaLw4y74NYaIo7-rePg6M16uz_Aa1NfrCb9ftxb7f2IDcoEIMQOglFR6FigIe-xgctCIq_vaTmr3fbAafXIefNL2HEv8_lfByaaGmUlgOz0eTg2RNHiAY5G56YdpEqAXfO3wepFPjfzgdOvwZXn3t6_FrUwMgCtLMKjElfSKGt9ZcrV3bWH2BaS9q8031sg7CFHQWet2dgZJZcIF6FGhkabOKrPLTiKeyoygKdAQCKKBX5KF4EYImL99mvhXkNgACP5YTkjiP93zj4Ibf13xPbI5z18WnevAhIUMCuDiZ-pbn0rCCEFROV0PPsq2s4xBDDuqs2p6s6yICabaO3QNq3CbikaUNZLg1e9YnNtCi1xfr4z7gcnHu2C2XBNaMGwk9_31pGCtOrKfRgR2eKaq7UD6Tkki6eM-dc2q7Jw-LHWMn5t_XloH5ZPsipT9FvhfdNWn0qYBN9sVe9XfvZkdsfxbC2Sm7-tg9JgD3TplSQK8YgkHgXjEP2gZ77Le50wZHwmDtNT20rHvzs7cCyDFU-G77suehlsFBGOTCrc3yjckOlTZRpmVijpFRtHPYkA1ZvtBBRpGQsaxTZvN84nWwXP4sVC2urXVCvbTTBbrPK4M5AH-bfTI_LhSDyh4Avg_eIBFJMonXUjgkRI4rUyK1xXnhoc-Du3JrtbGAB9i4Ed-3ymWD5buZXUYIg6EUVAdITzsAeJqrP6NEH42DUP4SB6flNPIyAjTg3lfikkcHvt5V9UCkxBYBdwrdC5ZnXgPR7OECsUsPcZtKM3qM8J2RICjXHqTzx8gJYMQ3c7L0kZbkSVA-guf9LlQrfz9GdgKSe6sNRGBbvLH2CemyjXO78p-F2SiD5kwfdAYkhIe0ULz8S4prsbiXl-qQvd90gAKVHlDQtjdDEOgTvtYbyE--6Hqvd9mKpuG3bzGnwTLZ02x4syrQ6NS0ekrEynx9PBdFxipZj-dz5-Ydjc-FWe74kj_G9VsHztfWg_hc1lLztKgJuwNNum_pseEJlqgpLRCLhJxTFchrws68j3M36CfzOFq4U7ptTkLEj0ZuHKt8wUqEqEj_iP4JkL2q-C6-8QUBYjSWGZZItIxvx8PpMUVgitYGvyx7p4SuEXoccrSa1FhbpFlKyyBZn8BbCbJPIgzuK_Fcltn5n2l_xDxJzR5GneV-SC0RKT_5vdd2Db2GFGaXjBTTg1wq1bP5bJIZoRCSU8R2Tc20ktYvgi3THLBtuV0fxriQNhzp3kLi0gNZr-4Xzd9Qz5x3JyBS_k1SRQeMvrUQghv2BGZOKHN35UMhTv9J5cpZGPASdts0tFKmyvhhPe0RPjpDUhbU_oeUVANY8kYtlwD7VN-HmnPXEaoAbVyX89EMqFQHNff6WR1sW1DlcPZlfgGh1QbrTt8kIA0aJEYO8pWbwXAKTxoF6MWdkUxxbMdc0m9IlNkGugq71eEXeCbFMDX1YRmYOJ7iIghkp3F3lPJNqxNNMXDKqkfHIPbo8eYB2vgB0yXfwGQGJyr_zYtAIXNIQJNp1M14MxhE_Cq0TwZtVIf8_KuWnkM_D1gGZT6NUibgcebd8j-_UnKn26pT-axRX8yVxwXDbRmczSuuH-_dXCijOFXsiPwc7cFgJK4UnPlITAXfKGE3ueDB-SHjCC8u8rCVi5Iy7wVAkeMTju2cDIqs-MVkqP4j2WyP9k4OAC_nXEMjEina-aFeW2VtZSx97b0Bbdfdkgj5qUCBi1bT7rFDGe_tL8vor4K9lywqD5rxqvztcsV3XDvoqdUgSSCX9jyHTSVTCg_ro_XiJj1XvuxL2tW6Yw_9ApAZNI3lOJlXWDy9dt7KKq3rbKQExXhpcubSUHTRGg6KqDjdjWxbCFzcciuOJHqS3sKDeZ3KEzFRUJP4xwg-yFnBs3D16p4V1rQ4kKeqyAU2tiznpxT-ez1hmLkZdvGrmCOtQnuNz9xstCkqPegAIuXCcTOz_fHHvqpmCud0chb4co1CvJc8i5LFUTGApkqsTJKst9fPzkENpUqP_esLhiCwGTCJkphz73EcPcUhdACZ-BfW6yy8aPVvjNIzdEQuAqGykOqYhZSzIEUdmeSwvc3-vmfwh0YiWzo8CWotaRLcM7iSrePowP2rhu7T1prvyWh2nPwJeHY2xtI0CVswOl2MVXNBBGFJVaVnaygOiQfUQw--ZYZnXP3YWji4xXlAGQzZNMOdETHi0L2DgBhr5AT_OM1X3qP-VLGB9ClylQIHQByUgsSzsL7LNf2Qj1gsFyigynan01U-2Ipr-fHlsmuQgHL84FmHL_8FQsXL2gukAxPDHEFVNEyZl-LZ8mJWynoJGrm09xjD99J8Boh_AuO6vEYrlq6x_aVagctMXAVtYaliPTY_apio9gr3Vpj0OrEe4W-_he3tt5Wu2OlcTMIQ6O3b-Q1MFHIzxCZphRZj6wwOGg15m3EfIICR1172hW_5zq5LRY0hWhppbC6JOqJ0kS1tQYXu-Yj8ulqcIQarKnPucYmkqUfbkA9x1Jcbrd6vbhy-FTZFmF5S1bTQAKKp0wdi_GmzhWSX7NryzBN4D5leqpbXq5JgKfcKdGroW_-LeKCd3mTTi-i7MgfRXbULW5-2Vi-Mhe6t05DXHRSdmlaO4TZkIfQ9jC7wFREwT-MoVTU-BTzHgHD1JLIWs4jFuTcjb67RU78Mz3a0ABFOjzWLfQ78iJ8xCZkxzd1OHgIYlkfin628SAN1dBoXI462v6YbLrKyT9ADFrbUow14gP0HDw6bk6eVO-9iFj-vdW7T9vIGTmr05_lJGU9_Fua9pSG0QEWXFIJDGtwdSAqsqOIx-fGIYcHQkEifXdpqY9Y7Z5JtE-0ufhXj180s1DxnXLqG6v5-s2036LogHAmPFPLlMDGN6niA06I-C0HOvkD51hl9QSUmGODpG6Pos6wKgvHdcbJ8eYIEViFV7RDc-RcdPXedRSi8TKVu6AA7-vq4N_wctAdPKEjPTsg7ryVo0qM0dXqKXQlDjsQP3xZ7p4CVUY9TBXUc2ofs19ce944GVSgfSBfJv06RdAaFUf3gMse9I-HZFh8W_1C8zxdVprHXpXRNPXvT-f2blhsHVljWyF5oT8YdJRfg5Be_AgZKEqy8inDM26qLUbxdWhAZN5-VWI39_OhxW1LWYyxRid8qGhhCEcRMcIzvjFUgvVrLZGLZ66DUAElTYW_U-Big6nS0KuRUYfgKyDOaeNUE7QCpvdbQQpoZ74vPJHonvLVJga6f1xNIPgWcDgCn9BaUkSObPO3HPT3QmRQFP6fNRD6ClsD2VlfXO9IIeigNxshI2PIIkUTmT5DyBTZ19gpL26PLr9VmgMZQ_1Q2ZnYpZ2QRSnarD_CzCqAMlItznvFWIUJYaUo_Sv1GwbRKkxL5g3-il178X6vnnkucd5SHuuZFhdVmYmEiyGQSvPH-3oY8Cq-8_oK_K8K8xJk0LBCWOOclN6ofJ9GUpX4xpXlV3fzP7-nb1oZ0kliikNYIO05z3wICGYxWAH1w2bhh9wJ5EBTn-UJ8sT7hCru7kKtKmoKdQSpmhg3layUbdwB61Nn3w8ygZ3m0-g0d1oArPRahdiI0q7SwgVermuEShR8gXGKItzfkTmnRQlsBfu-r6gjyVvTbRpZ3cOKLkqcPZhcpsBKDg5A_arKWi7lb_NR6ON5xpQgqBV8fjdbrqSXv4n-kQ6Iy2rs6Fe-ZD38y2g_YtxTtdDjogDLlShlPpkMUbXdFynYlftaeHM-2BnE6jTT9cCGvSSmT30DWJiEJUQZ7rEUstp5aNZb1YSmkz3vX-p66t8_DBBrhLp31eVx1MtIWg7aVTJRhEFdkdnCTsRVyZ0TvcSCNzDl0n7XId2-bqm6RgpizuKPuS6KDb-vD0Cx5X6qXPpxGXiqYIHXp-woAnZwCROUYGbJLzJzNNTetEQiLYskFvqRIIXyn9L-352LtH3ZrcfOuAcvyml4wvA7FLfPCDpoIA_6dyutPzaDwa_xOxuygA8Eb1QZuWRQTk-D2W2ncvZRV7c4Pw45ZYQC16ta09u6OY2yk_fyc-0cICHvDv5J5ZN5IvxmOD1HMdv-deqeu5Gs0C5uIre5EYg6kPZiomOwr7L6p16fOUOq0gdI0bOLg4XXKcjRm5ctcc4NMPuDiy-ddXonnSoEOrQBuOcZO5hKDOTUWd2vn0e236a6rLFZvj9p4glldxbJCQ5i4l_fkBTzpkdBxUpf6gQoaunjB-14zN5JzbBeOTmFLSgFFppWKKZ3EBDgxguopksbVMkymMsMC7hCQ4VLGURbK02efR0AYCYstQVS0OluXE-NNLIai_WnjIXQ8GXGiqpj4D-HzmKaNprHPGemymAxoLfadELdnHbcLJJH-J8WZEqbk37EXZHhzHeuOFMFoFi5qBpFW9emunCdJr06dzhT26egqgWucPF_rM2Yft-GH3Afe2gklgRkvki65oX_4tywnl9Bfo4H4Ufy1JmvjggGATU_hdfvlkR5D6c-0h9ywMavg6BwZkTHhu9ZDzW4EcICb6RvL0K9-jweDlyKv4wEVSd4lMDv5cIID_TyV9DveMNBZtNSm77w1dXtw5JYq2EpDXNTE7MdXb_Gr8AeOdhK3ibCwVUZjOcIlbxxNQHbqvsF2_iJfArfnihOvmQoDT9z2deMTijGkEO-_QqKdUgUfjF73bCuii1hAXiYgqf3YlBk13SHTlmymrp0UG_Nw10dXYGa4FTn9Rt7CGm7DOAhlzOqKnQdRndL_xLX6wVe7nn2qHHetdkvoSuvJ8_ts6dfs4GEwzrX0g9tCLsD1_wViq-k3fVB9tcUmiMOh_4NgXcKN42PS_mwkSiSD6hRJcHt219BPu7lalU2zk2iqRgeEjF1GtHmXRkbGPdi76CbBOqwcmBMvJY7OVz657PLgMhc_g5XEvty3DoAqUwbM0AYdop8iTd0ui6wQBLGD6N5v7w2qCH4Eq0sJdXGc-zw8KCnv6qhDkBKYR2pWcShsgBOJq0UZmttQy3hoNcqFLk0Dhr0Ay8j1Fir8LMQSW8hAuh9SXrEpun_NVdruE-FWZAJNo2Oi10ToAEm2UZedlojdn9jYlSPUbW8kM8zX616hktr6bjdyiKBsW4bok__9XgO83WnPbfmkaaTBG1uOFkbi0-tG5lWMIi0_DKMomoMGjoK5AMxu5PV1Ai7_A64LdQzk6haqtemyj_38cDlpfZWpf3ZxEuzq2bd651idZEqJjZES60V-KQot6U4rZh8M4BguOP65Vvi3x4QE6YJUx7Gfh6o0cpcXF4oifXzDctBEijjrOTU-nk12pl-eyN6RnRjNMHwpDycqd1-28J5sZU247dJn6QsxYN-NRS3_9vA4xWcBzTE7SMmQUA8UQR1e1QUky1o3RJONtv5pbJ_AMCVxuM5qrELufX7oftBNYmel6Bwxx5bMIA_k-O2nDReLYBFA-uSJhxLUCgzoHP7lx6pGr7Pe0mAUdOpSWkVYL_aCsHL4xbHkL5cp8KjwrxY4jA3l_fpMpTMCRB0btqGtsQOS40rjc2XDx9tj3RnVTq2JboVPLbuu6GTiCkm52ab-mhnFxsCW645q4Zgn-vMVtuJuJc4fTgKD_5J6-zo56VuuPKhrZ8reYmPP8HLzaw8YLA-TZFR_KW7_u1jDcFFjrQZV1sBQpEVSmeuD78VuIdxmjZiwSzt8M3EV48Y1hQaSYAIHg_KbGIVcTyjR7lmh-c5wQAhWDivnGSbeXdqz4Ebu8M7PVKn8k5iR-EMsZwfvLQ7sUNmGKebvoPF6-bTpJPpna-hvwnkTV0V5IghzUH-dwBU7XlFZvH3HfoDI89d8KxHSSJa9IVpZXOpKi5MRMN-LHQv1uGewkimhkix7Yofe5Ce7bN_Fm4NwkPGmkhs2sQfYOcVSPWeoBuLb1q0xGPi3-YNW4PrCdMnHchq-Jg7ClVnZueEvftRt-p5Ya0fVeBktw-ga1dHXPLynYB6ROYAI0-oGBmEImeEVmiE6ihbNVw5PnCFUbKvsOdgBwcC2PhWZj6wVINf2CNwkgrBomh4Yuc6oajWygxA9AvLLcvK8ZNrweJalpoc-y05mLzcJKAoveOSXF02Ix-6oLXYV1TfNHZf3bJjHX08TEQzPGI8mEhi1_WJzvZxakiFoEnkU_gcp9a-mGXQjIbcTBWUwxEwkQcTMH7h3NeulcmeY7Y80buAKZgauM_wU5e93F-olJBXChu99mCLv--plEtN5RVMhm1VERikE8Gp3SvdrFm5w5xdt-Ud0LuSAfld1xyeeOwezCznEXQ4HdAM-0GBdt6ofwBHazloKWdyQ6y1fnSmZkGoFtiZktUbxPd5tRRnUSg7aeDfsEAuQuGWUnbDAoNs3EWyS2-vODRzvEma-8bwg7uedG4UQ4-0k-jG08rex6hYohh01YrnjutCGsri6yqe6q6s9GsYvwblMXQyaF_-KgRbDyDW6QfJVakdsVxBHOwq3hYdXuAGqd_GNM3NQRB1Tk2PXcGYILgN0RbdLp3PRIDEjdYglpjQT8mWXZW7DBxV8E4uhJLJFPFY-4IohhuzG4rsHMi1s1y9GiDbS1q_RB-1h7I58Sy8Ci_cyO_EDzhkVF8-FO5JSEPX20O0BpSR7Vhg2xp3b9sBO3trQmqfWSCkrZ-6arnJoJGrb3rAD6QPYQQw_vmbPnyDRkC0eSyAeiXCNpv4dT_85KsEDqcok69fa8InMJ3wNqtSHnOkfCQqdPLciQ7A_DGtJefxLhIraty0HBLwtGRBa3xgUgSu_af7xgoVfaAo-k1wp2_Q9QgGsbVx5yodvlNVtxbPh_RwJvsqY2SzrahbGssdoSro-URDI__PzmyoKcYSLNlK_FQ_7GcjrXhPA2SP99mphw_7QbPvPby2qj7IdxRui48tRdwoWU9zC-jRHs-Wh_kL8MQlEQQD9h7nOryLweaxx42zshy8waog0zqLqmqqeotwxTQXQrYcAH3tq7_GrAe2orRJrfEu2D9cPTlJQoA08Yq6necpnyqjHY0mOC5sHEDeMwKzbpSj0EqL8iJhddCdIQgRRH7cIf0qNXflKQ5E6E0_Spc8U6qJ95nFNE3wEJlYGi2mBl2v00jYfglB8v3yujBn8Hnrg_vYR9btbMChA0aUg0CHZedukLNzqhPoSpnvTc8EAM1Wt5vn02szrweOvYRloew2zQUU5BXbMtilgaD1Xo-xPeLC5aXyS90p8qqH5bWCeyv10R1cRt9-gwg5XgRHK-xsCrZA0wHZlCPItbrK7d9p3vGG7vhNDeEzEEIdBub2asejfuvCSm1AwkX45IQFZKlCRNu4avdPMcNkFtH1WhJZmyOzlsCoGARs2gyrIFre6_l3nfbysK9KpU4ACGAo5F9WzdGclBZbtcueCYvxez1qRh7Vmuhq5akw8S-w4oVcQd0RySTad6UaB3v-3DAmlknJLygU0_zWm1GvKkJC5PuW1289FJvIdNXzT8I9A8nDRO54JdkavAxJoZemrEA0dPAoZIBtjHC1w9C68KmS-w8gR2KVZTUvpbqFa5fBuDUVOIAmp0_mOnFSxHJutpRnPZdySYURRIEdiTOgxotaXXsVDMN9zP9nbUU_0bBJb7m8JRI5bMKFtiv5lu_he1QlBDpJRiadIeYgJvoHAj6pCfb696XOWZvotgla9zeuMlIa2o9CWELksccsko9oRlReX03Rl_Lf3S6YL1SWWy4QpKFhulFb45d0CE7oTZbRr82eUOJxGmlsbEH5DpOAfTg9A6H94HhBfgVIlor_SV8AHzu0aW7U5JJIIETQei271aENKCwhVxIFeeNqmDJtNwBDXdHWCLpwUxMu0f108YEm0qnlM2N6-OJ0iMeaDUv2Dg5u_hRT4Uc6nxy7EAf1dhhaHl2lazpshKJKZx3zWLeMQV-uHnQVYZ6-FHa_lDFsr6mkQ3yK3T5efnWa89Rn2YxEnrT67w6zZDR38f3cGOIY9sOgj4-jJfkSaZKEUoTh_IiX1liNafRDCa1i739gFvwjDLnGBSaZXyt-4Yt3251Z3yqUZ-xSMwVsm2OWHyq91f4rDfg3tlwRvDy_79lKd7Q2135A9M0wEYyacpW6bbYVLKP1IJkqg913_fEYqmbgeLlVicEQaMNBfBpea5vQynBlak5reEvJvknl1sESgBTf5IRgr5Ww3MpIt_fYxrFM0EOXFMK4EBgBTBGl2ReVdOAsMHs362OC0CWq1C0Fp-6h_ditV2zU1xUxsEcpBQ8nQVXHhPZDk0cELMf63r-I5PRI6zL5aZHJ1yOrJ8MtgRaG1Xj-N2lnhQDQtOuk7AKhyik2kky08szAjrRhg2-EdwgW8QwGnRAjgAR_zm2fkiU1OokO1ZJW3UyVPnaarHMV6BvHJqZiFTKYTpVUhMHvIrw3J_zCGhgkgT92528BavyMwMpVPfd-3tyUG8z14ebwWaJ-J1U7SHcryECn-58IrXwFBOxiyJdwbNYzDrX9pkwuLVVrWMDEZvmGLVQg3dsqmpn0D18SpuY3w7u6AUJlGZxXnnrxnt4euyNInYy6JEVjnvRxvL0BGbgxMGUfMMDthUFuxcgIM8fLVAJSTijx3Thl5U4XTfy0a-LLVe1qbIeCxp_amuoedUTIoT4hoS7OO4LDyxxtkKxC0CBDSOzF1ZQfiOfPjYc3bs-TiKs35filIQBqEGvkoJVEqtQuN5qAHqTdVJ1rM8rs3Xr9-zc9UJgeekquRLy4WDLVmlyGo4Bz19lIUxyWJkjrllWmAoJldnZ-2wxaicfgxCX7cUSxv4gEoIOce5-qu0JyNs-ZLpQO2dP0RIx6djq6GVczvKNSLPcKTMHVyBIlV3qtEoz3FtQpXy7_DrSDrVvahWvLpbKFWRIKWVHNqYfp64d3Lu9bEF6zyKBwQE-GxB0c7aVtsDgWSEOY6yo2SuRENKZXVl1x1ZSvITI2c0ryCO1aKThaunIhbw1vCXSYZJWaA7QxRDQAbD3wFQGNE5z0cisqeMQkVXD758593rrozWsaH5-WkwyjUitIOeMLbEi_HlIeO0hHsA5nQ_64dCHacG1I7nSoRKEkVDbkqabrGAmyu1vwiZj5429GqB-KOTY_ok7KJxxl0rNJ3XDBtxOamEbhAbvEc9GfMNMOjbjrrFTfVxKMx6-sTZcK0wcHZsm8ElDsxdhm5XXcCWqFIUFn6aw9UcFmS0RQFOyZrL036dOsfhjHa1g1K94xaJ2DRjNBU4nYNDRV0ghzLoT1S7rIru3RlLsdZr7p9ytM0i8D5VNNfI3YvGn_N42Cr2Qz-HEw8epBldQScAnb67egTH1-aT4kXUcVnoKXbnnAS3hvaXAy1NCFNh6B2dllyD6hYmP-4NIXJhABl8GCVyoie7UjenE3AcOBOrAxTeJyRvrtJ3sGKSLvEWOHZn-efGxUV9R5AfeoPHACc972-9SHwVEc8luPQaaw3PG4Uas_dj5MMypaRGGHm91QoFo6iw0EIDMG4pRPwXQ3HKXufcXFU7dopeceoCT63T_Y4mS7Pi-p-jTDJltnzHI0h0Vbc9RfRA-MzWZH4gBUMg-uuY1LlUu5ebej6zZysuRhnKsTwmfn08_5HR6TVKpSp37l5szlJggsPMQXDMRck2osTgIpxt5iKW4SIg-yunJWv0T5Jx7dn5h6ky03KhOiJTc_ysf9N3CfastCW6AXxjIdLVpREOZrFPnRxXkw3wxiEL9uCjLA9YDJfJHX0BTY-rtqKJ7X_ZIUdq4z5vwYfWsFQlS8bPBgCziQCpGgIqHfnTWCVQuq-5N5B_mtP06ol3dkOd8AS_sJaVgDYchl53WDlzKC2rgweJFJUni9wK13eybkDkOFCzwCHO3P6i7kwNELKDRHNQJjbWQbWtAXSVdbZtuxn0Ek-gQ0t-EHVsx43mXjGGU8J-gDmIGIWdZ0xy_oH8PtmX-OH9HFpywEme7mboAXuN5UjhInMk_vo2Q2GNjCXR02gKqoaW-j9eNuekK74nJlnXcSvXrIkXQJ0qqhhbfChuLoEetDWumP1Aa6sIDmTJXncR2Hp7C4m8w6ZSTUleFW71mwS2-0JWgBlBNzS1DDwHpC9gPjEqNHTKAeN0WYIW5UnxHXMhcuq1ZbNyqE9DA_d6jAoXaRvaQTsLQuvJ8DMuzkVxjcaEyQfnWZrlXNE9r5wspILrt5_HzcaMA-kwjciZ2QrPx-7eRXDNDICkyLpuKtN6VOd3R5RHmXUpKsFeCYbTqT5Nf30lshmCn8ipGwy8VjCTQz66wpsA6LGxK_Byx6E8uC3_nr8CAWH2dEja1BDUECaKGpzDpwckaqggXezuvcVAtUGAg1IULq_qeUVEz5hj_RWF8cyAM2GMkbQ5jkLuuY2dtjQFW7VMhGm5scx0IVhLLsGDhISRYcOTSbLEvIwO0TozdcICZ2n0JfBzcppsvkJgRJxIEm84OM0FRv4nJ8masEp7BiT0A1rVWwDIVzz-pLcZ1Q2fetabPA_cUNoTUg4kjA6X8Xc44Hl--FPD66Qmsvu7g6oHW_v7CI2N51H4cJeiGM-xMEyaFPuu4RZwX0K4yMjNKJFMQddwUcQSv2uttnPbgpNTO30n6Tqzlo7oPweKA80hzuIQMChOf1H4UvJuSp5Of-wS4V0liIHuFhxrAiWBwWmqaJdTMZZIqC7taJQ6maUQlbIHy1S8WZ3jSq2AELTE9r6RCtwWCCqZzyZWLi8RQJB45Dv-c_dx4B_HyixYx0lKjukJCHr7UGW_75Lvu4j4CQo3fAkP_7fE_3w73-8ngbSWTTvktONQ1ukdcikh6uM7hsi4Lm0wD2drv490WRY4ohrRH9BWZLteOYF1tAAShapD0lscYi7rwLqxyq0PgRTsUpDj_7OvGKQ94oiMO9MAYe7-_wqmISvreTJ2PWtcwZ05EYubwKegzYNyYta1WZAPc73gGHW6WbbuLubWKolwYXVk0tTG8IpDIz2M9lxvBG5XLdxhC73S4Gnf0KZz7mbCzvgj334jdDnH3XgJKJd4rfpnEu2cFjPlRy4prclBwlCyvLK-DRhBN75denaRwlGXwl8mNak6BD2ipuOUdsRqLpIgZBFjOsb1DV_ZJasAwSRyYGNH--DZsBmzvPzdE9q-jVyrJPKyEgEeH7TGif7_yG_umQCfCF6KVGp5ZiwtQNYN4o3YrdyyRCyA_TXCIL_5M7cIKnc3VOxxFiQZRP1bLajKCACZAvMkmi3ecOHsRC2JHrUzIrsdbArapnTvNKYW1lyT4Z-nR75SbVYuBIrgQsCqBsR-GiBWAVImlLh6whfKqy9MJLE1C_eDGIcWH20HPx2ruTtSCS_SW7EdPLrcrSLD4jVXn-VVCVLevQCWAoEvYkSwcCs2RvjVfleSKrGogOJvYXZr4lc8ajw7vm9Vmhb5Y6C21Zw5Q464sGO5xIJ3U4SDYwEuuUTNUVM9Mb2bk9LJGlfPc2p-M0ODCJzhyQkwqhh9g44bk4hcmql8AvS2eMjGPX4dl47NcFP-xVh62hnOZgBfLtMXDM2eDe_wrX1FCSaq4WJFsV7jyPGWhgX-VjRrIKmL9UdfC6Tt1EzMcwyV7La9jfuo-l9yJ2oEVPHb_5b3UXO4_IYeo47vxkSfvIlPE0L2P7fP_n4nmtvaThjONDHfKiLFUIWoy0jRNJ-c_txqzvXAzBCDrrpDBd3WakAyrjhVESjACYsKf_efA9VyOPOrmnwhuBLf1hiaAmz04dL5ZTD0WhTk62VkAtGeyuckFtyvDdcUvNqpOGIKtlQTsCa0xWQyWtqTGXzl1m6Q4L82be8hlYsWVoX7A71Mrp-2aev28HMo1GhRTP7potnffUmez8z1c2uhoItm0PnSVwNS2dcwYtAEM7UvxBxxAljNATdRPh9zpuoDC72t_RD2T2isv_wRII8dMRXIS4LOao2ahwMH9IsPzElngYq_pntpdpegpnJF84YAsQUXR4BttPs8zX-BW6a86MhrYwc2TsWY-PBk0E17y-dPBiki2kHlgkBEvTxUEIN26KjAR1RucpJRKE6brfVpra2aHbFpCnBw6ccTlJPsYg1hwuiaZtoe8zEDvfKxzbWr1j2k1AuZY2o0xGBwXC8yjBZhsnK9aTdlfCzXTEnrkW4egKXVj2sylKpFAksAaib6puwCYBL2QFYrbA21-nhPgCB-ODojfysA_oDSG7LoDh68hiTKyfCu7Z0GImqsVM9vi9kpX7cggV3oGvTCMOAgIt1u5ZVwVuI2bP55IegqbJGRQ10DYrwEAhpz5RXdE4ILjPm6ZUeBoAT4e63DrTU8ShUfZzr5Vf2JRVNNemZ9SClqA7daJH3YGNzCXF8AVteYH_HdoeYXlDYeIT2cRiNsUwgCkJo8_EwveC3wnc_iQaa4g5cvCkDgvVA9ek3d0-LQKGOQKS5IpWKuOuYjazfVmP03B3TQqiwwJQB9AJpfYIaYRDO4UFdYSE2sW5GU61SprgZpmy4RzBlIZdwO85TpelR24voShZw-tWYrW9S9nK-SgVDjAe-xgqyNJeWHw5-_9aIzklqoFwVIPgtWahQDVfBsXNDLuk8hJRP77GORkzbkAAap58OcI0cwYvNziHJ1SgjPiTrcaK36OUX2E_sWHchV7yCY_tbp088Hh4XXOKGTxFugIutCn2ajtYd_iRlJurhEEi9DuEsQ2X1FVUOH36aIgUnmop7N43rkDWhNLwRzgTsgoCh3kaI67L7nGyVHc8Ob-Vj0sS8_bx_2quiBG6MuJH7rnrmjRvFxy_vreXjmRLCn3M8-2W5-0JoQjapUhKo574qR1D42N68xUD9zCRs0z2kAfwvNpWJ3E55z2JBBQ1w93K5zt_xBB8EG14aE3-32GK6M3CbEM3d6VqicmAy-4Gmmns-3ozoKHallXDibxEg4qJ8qMBBLQwNibA1naQ1rEc8lieGyIfmN4mbs1QeiZ7RvRU0juO5HSzWDhQItBN7wa8U8TE9U_FDLZFRievjI1wbDzWwZ3PRDzV-KQZf7necTmiqjJg1xuynz01cqQ8exaBs4ajpurABZyF-TGvAa96exVNn0NhQsfBWQP3pOgfQ2FJQNOG0XRdYwAIyeAbBDaBl0yn8v9OqzTccZdih8OOxxkAd7wRMkRsgXBjhT_AI9nCvegnbBdtYPbMdDwv76wlLdyD3oTHp8w4OsxjOw9gSVJLR1HFToBSbqwc8COcSly1s9IC-OsIiLh1DKg4qFxCYv4HbqjgJ3DpHF-XT-hvOqEoe9Whpd_0GJjO6MIUvoVpEl4_u2dHgMybOHg-UYXpAog0dkkfktuBEwLp_JtOF28JwACwLewfJmaQmuft1PknMs3BaMAFvJh4LTxgm1QdE0dkg017ASrfhbQOqjvLidJGcbNWSOrf9ppe12HOY2lVkUY1OXKgos5XCkgAmHhe_1nGGLY8qX5hEgID7qhhg1TU2R_VptvDjNIXWyyU-GWIqGaUnuhmLUDrvDBTdLU663KWSOPIpxKSZ41b3xyYVzXKN41t5UlGKnr-Z6pHriFmhN-J3nIZpXYCv-BMl_6nkNqCXnV91xCLb2F93ogV3GSn_hLZ88rgCyBD8hT2FskUdcTYDdPImpHJjvQG1KS5Yh_c9n75JS3jrKT6rI-siYIeeW-sFHHJ4PXkVoWU6bsQ_8axVKMTrzNHWHQxq4Ot72Nepx1audwUR9m_07uaO957bFzAj6WQSAjXYwnELrEjuu8JgYiiALUnI-gEERzsNFfB2-Gn5Wjd_xQUmlTIrsi1uDnH49PrE-Xbg-GFSOnH2Jn2J_NmXNVA1Owh3cPoNer_Uh3MBw6KOOoLpUhS1l0VdHDghJXQKKX7LCZJTtxpkkJnGRun1WbZgvQSx5kO1in-rmBi8Hb_NXUylVcKVk-CxCGgVpR4okx7XyasvWJwQChpGzYLlO-YODxlFSnWu3ZosGg0Ekhqusgy7UOntRnISffv-Iqo8yivNYeOHdM49LVMawI1g6ko78RETxuIOMY0rhmpPW3DVRbl85-nq_pl6SH0To0FBFvKFr9YEK47uy2rLEnJzr8HNCAN2q5J8SXJJeZJetUpvpmqrcTmJtJ-olvY4xlpqLyrQSCb-sFxPuPJcmT2HYSVP2xS_WxclxyZEE4cNL7mXp2EckDcMI2xXRGqjgwgxgsgTxeHaOaaes07OkQw3ctXTtRSEsU8gXyuclunfd4AJKvyQ6gs2Sv7BR2WDErsPTw9bdHoeuNqaag1AYaa4snLfjRkhqipOiWkPx8IvrZ3QCblPM8KKBvngEkRWelk-Olf9cnbAwHeNPIJN8W9Eb9O2_wgFdOzQZZ_vrsbqdH2mcC0jH9hNAgIDxxng0dJwQBHvBPDPgibef84R_RaFl1KfrQLaXwVMmfSFVyWkBGEjGqwGtcOKsf1-JZ4BEe8jnm14LrCgOUBVbIYclVscdUrb4RL5fLWrDwm5MN5x1pIJ4nrXcLSwLLn2IiXDjxloi223cT15Y0-E83z5c6WHo6pFQAeT2fy5OiFeSzW7GcgyrQpbrf-i0ydkA8mDb11J2xJs5h-WoeNwHV1rVaCW_n56ownlwq12v6pW8RFHrUVFLwuElph47dKxLT97lp8hgop8sH49QlSxY1hVbIN8JAH85RQVc_1lDy8rcfjsdQtAIu-5X82fRvSohtwYSLPmIWHZnqEb9yAvDQroHKHRI5qBOBDaryzXY84gloil-oX1XKhrb2lR7kNjPsLtdu1Dkbd9vTbULZ2jeaR2g4HV7ljmzxYWW8fBxmRTtOIQR9CwLKFFfl2LBu9S10MCpRJa_sWpjSmVotyL1BfiP2e1SRMQOwfyFERUP7LGna-r52m-c4s6U7eksY-o5RODDu2eckOO1e7f9HlsigXvMLQM3it8lgzKQzrH4jPOt47G2Olil033mgoIVi2MXOEVn4j9X80_evaMzeDhczIBkM8Zu1m5pAH8fMUfIAmNvmrOupfDC-khkT4_ZvYZyO67MlBk6ooy4w_conrNASj0IOA93-Mi3QhzG-sW-LUqGkntkBVV3Gezsu37jyn8zBoksBiVjLa1TI_iqHPO9KIwoVp4S_YxuQ22RTxuzTLXHjodRSfeopObALmk0sAKtJSMo76Z0-rn4VRha5CkCjqhBOplLSX_igWIG5sBrfxLpXL_MAnqFy-VIuLyfsjQVH2yxG8nW2L271C2oomjc2GVLDupFGUQdhOHaHTErV34K_sIMW_cEVCpQvGkS51t0rIzmYe0onqy43tTygOjWMEZH75FFa8fAQ9ohtH57v3rWIXAIS0dVLFad_IcystDJAka4zh-7jXA0ZiPJrjSm_rYV3kjCzYtjwSTPez7l4c8R7VK8_NZEhiczu5t9j7VFYk26w8CirsmcIoRJmgSWwiuRwkJcV_TmwlEQBPjow-b0lfHKt7zxCURrr-XpPHznzkje_AQmsBnsh98hsYKY8lvfN4VmIbVLAINdpslg-qTsWGY57nZS6xg0KeSmVBOLSH0d_i3nqiVm1gV8Poz9uDCZECXK3-2ORe3wG6MXUd3uBfTHFvJ6fH0vhCl2RzZOATHmuolEScNdgljUVz-kBHtXFjcVlXo0gIBpPuSBzRouX5Gp399_rWgp_vuEMqJQmqBV7kEAsUdVtfSRlY3TNR7KA93gGolrxd0LxCgJoJOOFi86YXYNtmvBGeRWOH41SMM-sql0oxQDc-iRX95GGszuvudOxcu7gf1qahp7Y-Hnz3qzhoVhYs4yPsWNTA-SXEsiUKEfobCFNJ-ssr8e85HbjZuj7Qp7hCQt6rh4266D3Z39fzGeX5ImBz1HBQRk6kkl3vzvan9mIV3Tw2YgjgJFMr8kL9dwAc4gMZCZQpYuAqg2rSovui8n1yu314H9l3RCnjTAY3le19kdFJPA9YEyfGnSZv8Tgwhpid_qNiHhY0VysBQW2ENqRwYz2rJTAvOEGRH9LmtfyAhsOkQ5pXaSzOCT34BBBTmswLIGmL2oRj7kqUANKAAJlsrUOVfVEMQ5rawZLUpJ5WxX4oBDPbxUIBCTFXAMRD01rJ5H4EjN3181ILKDp3CjC5ggXz0-2_GSqJQo8GB4J8ucYlkt2lM-VpzDGGGjQRWbyr7SQjh365_MNsHSSGbxaDSR4zMmimR68DwLcbAG46SF-p83wNrODfmMAtUvxnLiJqpalymTzicqVhRHGh4KN2KsGw3G5hbS2uAL4guL7hKpYd8LGZ0Or_gL1Qpig9W1NjoKvfZVV7q5obp__CuQM5IqTqplQDJ8XZ4Y-Ot_TBoVc4010qt0Liv8eYQwgAbZXQVP2fd6h0NOQHbtLzyHR5qtn1jH8ImVZqbosmXmnQKE4GHM2SBlZU_4xa66QPEWUKaZzDQ513rnH5ka6TYWZPbHuspulf_WuReK_RpahbHB-iuuC5GxmCBpTGivQ0bidt39Kde_WEoBwM510MyFftwhgk_d4etTdH0TVffZcAH1PsaeaKzl43NQVZ95hEW3QCwppLY3vRhg3odrQiLV1fSysh8KJluDV6rfuJJskS-bU6UpFMQZ0I2LIecQBYR6cpHdU9QAx8OEi_0zMhtpbD8GSlOio4wYZh9u0iA1NU_HtE-I6h6fhxwG3FqPgZ8P4c06bEcOs7KYZjPUtyqhwd3iOGzUyokict11P-udV04Nzv-LhTT3KaUNYF1RRLtqBOsa_K9u2JxTd_ZEy5VBvA6Jq7_T0-ZBsrlnNW9tZCo6cWsVXw9a22Q6TeAlgMyu8mcfXilIZW8AjuyBy8ko55xWdScIQOsWZWVScYuz09ePxY9pyOrhoL5GiAaegcy4mAtY76fvzmbk6EgXD1lV7fX1vLgJxYcUTuv2zLsFZ851U8dOvoda_nCz6PX4wcoKXxTsGDjp55T3bI8btWrepqgObN57NKxABcLFkt70qZRVDUpUcqP88jCh7fgSAFEnp3JkGnlbXa6NHv6xzrLc_NwCtXHabEHPwvbJvpgljPNp_xZpd-VpQc1e9kH1QSswLEjV17W9HdEv37VW1OHDTd3gbHZRaZEWX9Vd0hpsE6OZChtgZu8OKUbwNswexaWSeovjdA0q7nhiNJQAVZIeFNIxulMgobHILvHmtc5UwEZcZam8x1xDV-oH_eAXhaPBX0cK63Bj2rJ-8DwmDGDcQJAFvtV6fUiqvL-JIT6XQRGl1jlgXRdAap5dfqZ3A726nA0AHC-j__b8K4iuMBDyiKTc8Vg06etaPIxKZJWth6ehUa5uffzNW1SYHt8BgwAg0rfG_mcicam9vw0R6BQ01W_NfHYmPfA5XOs-QF_7LM9be3hEQpqlYMi8-mCcRE4qS3WNzN0hrCr8Ef3cqJ9DfOP1bImC4UQ3u3jj9Krsrf37mF2pbK-sgNS2fEFYRPQKRabpT9XAuZooqgmyccZ2ga8oLPgMsY9C_u0UFRGdBaiWjzqwaGIxTn0BpICtAcVDqOMhYneXYJr86eeu8xzrmANsNeagZLjqEikN4hlqThvkqNSoN3Nn3N5yZoW9Ai3Ts71l54tJts-moqRbrk8tq3lxcjMQXulYNliFooH-DFGSP8AT9Iy13pa_GzpWgNyaKvYopj7a9QsRB0seu4sml4y4Kc1xXOIGB-hiGlNz8YBHKyQcbi19-ktJzX4ig9OlToUkIyYAdy9sqT-LwfDbWbnyesEHm_4bSE2ygeJl02kHLhVTRU8JeLchHO_0gbgIDd73PcP6xwjwzFS5NumXKriQoHkc7He2taehEld2gKotoacerPjhwEJEbk4xRSa1wU5qpIEJD4pIwt5FjHRgnh6fmKudKcCVn8UOvlq8K7s3BCsmpgwISFNDoatwwGdNt1kizw_RZBpZsCmiRDPtknGZZXMSE4qBt1SEyq_Fykv8eqplvWtbfGyOYGItxk_prMNBLL-td-u-_UsLuec0F82uL4O66pZ5PBVXoIu2nP0jR_wFIJMZgXI4NGNqA3OqAU6HKWtXH3OwQxLL-5cSrJNoar875P5Hko7o6sIcx_vaqo3izMKC2IH7xH4uRRhAGs2VPnqOll-vzaay9VsIsQ1ndYd1TsUEqahr6yNm06yq3nr_nzI-UV0Ma3LEQiXwt1hFTnh8Q2EUfpkufvXAzC-O1tccwb7nOZ3E7XRcQi5-Vjdc2vFZllBRDWqbwpqLrveWWSb_T-rMLidnop1VIRHeYN-0oosjLAB70Ft-2Gm62lQz6p6g2UuD40VKxOTzrbK6dCrSDkA0F8ORPOrkGNpTBLVDP8-Genmf_kIt_Q2tOCMIcyaOOFVlpiJmBfKNvNul8KDIv28-PnPvkDghVeLA2Rmvn1dFroGtenc3Y3tGcLu461L1VkBa4sBSBUueJYMrYyiTA5MqVho_sLN8aLZVw0oI4cKaPMac4UdT-wbZd5zVB3eyJJ_UZ-2sArFnSqqCh-PDvF5oVMmjh6pksIhuntReGNm8kpM72PREXEUw4pVkh3T7xa0oqNlhRF0inL2xZCFyS53SqCZsEXlwDR7k-xqqV8KmyJkD8mXH4fkHVAXnlkHm2Y-46HzNw5NiJPZamhIO1vHIIMSHpb3sA2rp4k--KLod1VDhivN5wtWgS6XqGOnamJOsHa5rhRr1NRjd_bWZUnO9x1NUs2414zfBqhDkrkFVbp4Zl88sgmEGNrV5Utsjrt2EMk6WAAjNywRjtp6k5axHzvKlOXQc64V_JcBhSYa2-wF8gi66Lu5h8u-j_taZefZ2jexiAYOxbJdHfPodcmu9UuVjlxeOzY9zHBCoUryR4vHACP6IppKaWkPrVDc8Aa6UqcPRg4yHdZjDZE8WOQ6ooFoj7TMxfk9l6Jp7d4wlj9OlOo3jivlvgoRKQ4kQHz4u_fGuRW1k0E5xbtfbTIiwIgmvQ9WuLOAogpk3iEs2H0AFcsDQ2Jtv-kOZE-_N_BF2XaSoQsbu9o0XNn9RJNI3MLRJkgHLydikBU6u4QUD8ja7fhineiryBJiyqYVxW202uds5g3R1SL5_xuQXC1mOsdgEtXsDz08hBOXpkYi7l3tagMDtogzF13t83BaoR_YIVnLggnu79esID5kqJ0B_86wtO8Qtc-spWZ2U7gZ65PbtGa2czbBl2xI4fsNoYScx8Yw7wAWalNjh8oauLtKEdnHtrKGkY2gvWrShtEs2AQWBIPZm4wt3f3whnfiHDQkWPxM9gYDm5Ne6x18uj8sSREzeEyauBZUWALbhvojMiuf6DvfQsH-kEjQ8tLP9CCeFDnP-BHiDDJMC2rgtJOelJCI0wiDbDgzPW3RfA7KLJiluXnV4-H9eJfcAHnqDKPuiOrckwmTASn_ai9Vw5lPsU28aosUhJo5f3nzj4hxIn48hpYvgL_WpjdHlPWdwhMP2mTksA7gK4QREuIkpeqjir01DmACHov8crNAs_2kL9jq8iAzUk_ZawBXbJCsl0l2_vhMJRSaEi73WBKWeT6Eoufs3K3RiuBwBZCB7gdFOYswfzO6cwzac6kXWi_LL4bEjva3JRHxlc4xhzmSTi7BMF8KdEPc1e2FYv926an6tiaG1fLPoBbpZ8z_VZZbnb4iSx7qaAOR8x4E1JWznMHSjGkgj4tC5_m2piCYmLCW6hD0qd9IZXq1d_OzW-GtfOZVMiEVIAimvrRovhFHy95OiPDUpoebInpfhGn8e7Q3ggLJ3vxtUAMRRGpXpCNvaES_VgfDWBLwn_CrxWdun_Yhtv9PhyQva_Jy1lEBTOYQahvBQP9c1LVekKXBIAof0abnMwXINwDgWQe7eyzN3kngvuO9ps3JDA6OOhUh5h_0RDDwQ_BS9xV6YclozrviZRhTLNmXxwtAtOgEV0B029SPNbbJyAOU-tuqibgjTcx1Bjo8kDicsZG5cf1DSNC7AyIZdXV10gCKH4Q3VWIjaFJiXTBsq_EaKUnfN2Q28jxBQn4Rh01AuVj3bnHRc90JAs9vD6VgkgGMweyMWzO02_5o4Cy10oY9is6VpMQhyN_o8ggzizYog2i1BcTOUBA6NSiK5k0SJQKud3BLBSw7iE5ND_b_-V24KS5eKjGgYmUgr3XSddUK0rG4OYYJ7oaMo5AJ3JnSghFif4JBrM9XZEHH-l0SuiRNkfDb4TcCREx65YNnDVwXc4kyRt4huXHhni7wBRHyuhhuoQ3fZKoHJTJ3bS-_G1IXxr7pwQqSNWCYsb0fMxalgezVcSaibLYjbhAAc7rB-Q7XXWygHuMYyuzSTRP7971luW3Idn-Ac9Rrx-Adda0VCYBLwR0DkmlEvoulje1hnhmOKuFQsBhhyFtlc_yoj7s8sCstf5tmnsfqeXS0fNtwBouzT6ivcDn09cpjOCz53BEm2wsyFl_ZxzkxtqgMTGJ2-xLwDK8AqRrfAnqI-91A9h4xp1bVZPlpN81QlbLQoQ6uDy9uLmvNgLniIVyEsxvpryTh9bd_VAjfByxufslFc6sU88r_zBSb_dUoSQpoSwaN9WNliCOGMaJ_Q4TT2FP57vTYyMxHVgJS3zDWLwrYhWdIByBget1HVWAxausPrwZd7y8aBFbut4BFQqqbi8U-7VBkQAFJt5GDOEq___42UQo98cW7j-CNge-LKN46aBKiERbcGozfeem9q3HfRNiTumSDY3vvDUA1UGy76Z_0dF_PUxKUDFWuCsV6dLSLzwHlHg2xa6p_qzYr77YO63LLXTsFiWCX-rV51Idy_HT1FVjVb9sSVOnOMH8YHoyZTAxZZdU-wbbjssW2QdMmeUqczBkEmI2vFZkK_-fvyjwvTOv_SV8IokG1I6rh94j-8wCPOHyizgme9ZrSqFT6vG2-Aj4na8IRIAneczFrvq3I1dAO36VyofzVHp5KbB4YGxbWp9Ael1TvA5XvrnhE7RDjiOAJSjwYP73HWp1R0Ps8fl1CBNviYafiSvDoy_lJ-AmShw1PDz5ruPRBfbFKWCXJyBIPl0KuiZvYW9F-tvzn62mF-CiU4jupulGNvK29-AIHSVr9i6M4JdIbX-ESLdffWT0_bFI37bxSmbm3tVr1EywzvAbjydkKgbrtNFmnyRCocYjNzTOvWKwPfCWHFeO92Vn65rVyf1NL7c45UlF3y__iUlWcU6ZOjTQd5lMa6RAcOBl_Nyc_5LDtHEvYPpBanuuuFLxoSM-RGZ_RfasOgGVgo06qkOQmAxz54S0JGhLWW2HKlBDByQJX9l1zjg_BdTYW4xPJTTSiKUhGSiBu3l5KcArp2lCyJn-3D_U1UQ7qpzokTo3lTXCyekWd1vW7dD7AzE8ppunDSQOCR1_onsC3lBG5fZfQ2kcun5S86hnjd4vlmeHW-6cskf4EkMVPk6gmd5baOz-Wjf3776wWhNSvxzYY-uwmSXXdbED7axklzSMOm77LWe9n2X7HDfkLSlL2qbjEzWgH0Y0lE5TJT6Vyqb0PfMw_x_UY9AJI5QdQplcSOJFxo45fMGGT16WMcYYh3bKo7vc7_sTzX5irFQj5AorIqy24M-yLl4iNyQX4I0AJQloobw0dBLuj2Vn0hhomk2FZ0oXY2gbKgPnpqjgD2nUmYUPrMPnmzMJ0-AxCerAbZn_IbecmQE6zHwj0Y8Drxk7nCUpi6_egqo3lID2qkcNXIFttC17LVb0qIhDyuLjexb_lfoPfPB8fR3R52_fxpGFeeDYXScNC_qLWt92Dqvey79jdJmstP7Pv9RXvi5ENDYmBWZjDFQBc5-MH8PIc5Oh61gzePoh302xB__mgQ6bfLIy9MvAJ8L15OxHbRix3itZ7ZHm3tAdzPxQR8u2IIzAyB96oVPZytixEB3li8qO-IdPg_1AOZULvPQs1sPxLLJfwMjDgDeZSQoYUqAIwyoNjUrrB8zQX0JoZFKurI5tlIkRYTOq3lxRNfwZL1I7x-Df8JJgOYnxmxNYmpxLo9obiLERSluisGrSevOA12OBulAYfWIZP9TxOAxODepmhfQKnTwwuZaYILkg6Mezqe25vpkO2gUVX_xuCYA7v70UNcszDlhYVnsD59AQ2k1ryiQx2QOm9K6Yx222_X5uG7RQhz7lXvhjBW5McXgF4OCSq2CtowA0nPYlq4wupbfABWXpuvBA9lVrsGZ2SDNEzII9rkz1QgOENgA-Q2mcgczv2X8zkxqBwRdEPtwadleBDU5OGRpzIM5EODbJdrA7miA5wc7_GrPnA3ldg1ZhCIrtz2XvOqWEIStBWKgceYjvrU1xUM6MNOrr7vG8LzzIV-el7f3CC9JGiS5rlXKyspjjzaXLQyrxyuQTKrBXKdIK1QnrAQ3f346TVwiGGAWlAL6_PzO70EPQIV06mcLpqhNd-F-rYkWtWPyw0PwglJefcybs6BMLz1lRp_HrfTw4IPMrnP_LwcV-oFIVIy9a5xEzojef7V-H2bqotBQcYfR_dBAMM5g7ymFBOBYWws6YRTCeRyv3oUwNpQ2ekbecvx4nM3isf8IuUGjwvuw2jtRy0UhiIsD2MTXHJWWtFRNcuNuWd2LH5vrgjjmXvZJOWBC7myYrp23jtuNh0ww2PZ4NlpQVCGUCVfEQm_w_aDmprQObA-FmhPD1ujNi-EkuJJi_aXudQaOn3an3VLwT67WLmiBkXs2zpjd2MyZELuu-RsblQk_Iz_wqQrNWHl_dPhtEDaGcEmn6KmQ6w-AdQ3cJODaZi4ya9entFekCDtl2nszJQGk_9IFKIJJqLk_X0f-M0yV3MYBMdfQZnJBThrRWTPQBuQzR3HUJNSoDK8D32komHdDPmTrikXT3fFrd1slKqZiEqia3lqso96BZxQMDUpQT7DC3UXMs0OwPPLnZYfZyS747Y51xVVq_vJi9mMNkKJl5DI9TW2Bn6_xL4M4TijuCSmg_IIyPGbjMhz7nengGOCpJ0M18NwUNCCX-wKgJITmOEFDaljLCuUBmfBnDPE6n1IuD_85CfzpnDuwVySAVEiVCHZfApo0K5k3rKCCC-jGq3sn9u8JVkhgGIYz37n9asOBqsP-ea4ILc0422gc_xtBt4jLG1Y-5rH5gMhNCvqWcosI5FEJGBtgj20Xkp39hcZipd31ES_CXiGu7Tompj7Ro8eML-JuXu88qgoMmieAuAmRKrX7D1Fw25t02cKT9ZXjl8Jjb04Ynu6pv83-3KCoOfsLjZtuj3LOX-mRaibcUse9-ZNm0axA0u6dJf-pegBxaLXpub9ebyt73JqLEOZ5wNL_w8djkw03RNzXOcYAUuzHAYxpbGZROn8WUE1CWcZgKoEWtg8Lah5WhUH6F4KwDtOAnZgDLphwEH6qRrIjVJV62Gx_UUbgqKitI7kpJDByTCu-2naKed9byVCj1WdZiNLMqQbB-qxM74ywHwSoe2ZFMgdwQfNRl__idkHJlBNxd3VAi62_yeP2z8f92exB8kX1nD7lbh4TlvUpQm3K5HzGS6ECJXVwbFICOH_rKlMkSRqpXpmET1hLCHLWW6sFVOj8ndro0SCGq-XJD2Ka4sIJKD4UR4MQXiMZU7oIpnAEDi1MnHZ6bZzA5PJG5UqfgsqHfvnBOZeB-xZLP4KEABseN_AY0wLAo_mE06UkPZ8IL1o7bcFTwhFNLqVgl4UZ-lWHukZz9lNEcXTMEMb7tgdw4H4Lv-1QjLEzo1IgMrTDf8QA5VrSY66kbndBhX4O2n293D88tCwYC85M030raR8i48tEJiRQqdX3F4QRPWfm5XoW49yDOSdd3CCiEhWiPbGzZFtqNCAeq14zDeWcZbxsAHx5ZHwwIEaBleIwKh3sSw9DfUErb8pwtYl8t4n8PF1RpHDqMqLoI_IlY6-Ae9LQEVifgFkguiVwo_5n14qlKICTEpo5MjO9tjkRgdKhaErILLm5oPRLpVNZzLGW42OFtN58uhFRjuln0mdy_vNe8fyXDTE9H8tn14XdElpdfkegq8TFJQUpqfnFVWSydRpUZcKFTRSgAUU8hg5cufdUTstIOA28ZNb5j5MBlCaZjm8N4HHbYRbcx7gTe8yH0vale_on0c4BOedK61YHS_yovVzrqPmWDC8u-MG3UwNEaosfiQzgoXDSrJz3hJvXBLh90K_O32BBl6BCUl15EeMrjS4JuMgFJA6tn19aI78w7YHPsGqrOxvCe_4dIqAo6CYJgFt88GHAZBu9uybi1aKgkFFgsXlboNSxjDZeAtRZ7bLcMczI28eOy0k0iNhAp-LgzP4kuFUQOX3pKAk3FHKmtk4bsENawMgx3eewXE5Votk3mdJaCHi1trsCpMqhRyBp8oyc3W0w-RbBgOJyM-q7JSAINBUNM3KYcrCrs773PNt1nS7ZiGTo2DQWNvUlBztSHlNMqx0go7KFANMR0UbLqCCx71gMkWyTR9pWITcJqoWVpCpHzSMShAVQs7T7zZ89oaFrcVfAoo2_iVwdzzXQYnVxLMRJHXCug_JTKcU5CRC_K1yBAMgeaou8rT5MqFBx6NO3J4JgBgvVySi9c_vJNH8YViPqPFLDHtoWSMAZlUvCnG32aj-4RR32RyPf_fxBZX-kQhYBcYKiin5ugTn4x5cQ19AiTEehwqtTKf71mvoXYxq54CZYQh1Rje3QxF1lDUpTZjMgd_lJ3okAAy7i8q2z2iUdvHdKXkMLyXOywbhtHZYjI8KKVJaFW5EH2SJOAj-QdYFAtPoXp73bMowWkFnmSYE1tmLKUq2-YPiHQ6H3_pCagemYOT3gmQZdOriYn8lXhn6wc380S-ICerpVxfmncqxkil8jjaW_1Tzc0puVjvyrDbq6qiHeHw6zOPamQqy4BNrAclwMqne12SH6Iiyq_VRaqVjGSYAAlqq1ZFoTRfUfNQn07I0_7tJrBt1llZhBuCUAPNknrZXfQ28x6T0lydzQCgv0roaRM_8anRTBh3zGE9tCiyjoIhZEcgkOO66VX0PRkrsvSSg6AJ3u-HiQDcEliAJEHXBDlaKnxZeq76U-feb96l6RMqkaqHeQIrzScMJQQZhV_kBHSLHq_wKfnevzslGowHUKt8-GP7txCACVZFZ07yOF8WJwpQnsE5z4VUy-YDEwMo3xl03YI6mlaaz9RsfS8cObUKNz2ZeMAmdB1BKSOyJifGdDlBN6D5zFglSF0EESuPUtFJBhGWTwjnmXT5aR9Hk4Shd5SzLuiZsV8jxzGT2iMEfe-r47kJQynNwja9AjuBwbR_SYyPIkVpR0rht01qEDS2qM6e1CYcilenp84W5GclCzO0L5E92Jbuu5HQBAKNxrW5nhBWt7EH8SC7dLMuzCfcGdY9WPIAWaBw7WKsaKU_I9bq67OlIzBBhSbV1QqcsQNf15ABxvpxLbtvL9mFDQFHWEfBNLLkkADKt-c6TFbSF_7iVj3gfO5k7F9uiAQRolzABT13wmKfXJMbSks1O3szT2ohWlbns-fv6rdk2RwIFlBhOkWmjFAq-C3GFiUKTrKSOvSvw-DbWJn8pddRgjqh0jvzy1y-jW2QUkCVDPfQhwD_ApwQBWe072rfmBkEFS04xUI9Oco_Yq43LvE-b5wPFXqdYRUOZ9Z8GgxRSTpQV4APjUOoC93I2bo56GuJEXRlK_I2WC9rx3HaI5k1O0qdth2MfB9jJiZJywjffAwZEvgOeGo236BlT3Ai6P5BBAlTkdXUPLh_wOEBSBusWMgpr5tfy3TmBXFckHGNyCC09vRkz0ASFdYMZboq4_qXqw0booTgAhjXa_4BkzygUrbtmg2N3Yj95NoIUmjGWfBrFWl6TSub4RifRyZIRK0ANAQHRlVvLMB2S-rzyK0PsdSzcPZk1VJAx03b-L8W4UOghKJ-j9Fo6q31aemNiNHj6y0Gl4w5v_7hNadClcvC76q6ZepzNxSjE8ajap7uf1RV9OF7jpFQzJ8ZgRnVUzxq4mQEDEY6LlrzKylUpI1-U9iBOPDlw873zNlBErkttDbiFpHrD_gaH9Y17ymScLPSTGpJBBG0xoAab7jPExKHPUXrVFZOLiYZIy8xgDvBe_FiVfzNWM8B8HCPhg3G_76ex89LBAlfmU7xkJVgN-n2E7vZpaK-MkXWnfj80rEuwOCJy_76hugvYr5tk-PZvW2XJ6-kqQdBPydmWbdU1xSRosI1HwuRhbh_iAoKEtVt_wh-AALe0rItBfXAFlD_LIvDLZ4QkQQFluoIQAtwQoPrm5KuPcnLOjUpQzLEbnrBIWJWOe-qHxbHDiOu4GsiWnHzmIP5-q4RAeIhyNbm_zADGbZfEplaIKQcxycSKb8nxOhdYT4AvzkYcfffIqQkQCChhhe-fhQEAgkotdPofD-Buei45ZuH_9Da-g6jF8LXiHAE1NnILOkZG_ZBn2OSfUVZ8I00g6mRAsfu-yStTSl9ZG_iuO5kegPNB0YHZXV4Qy6XnFw3GrSnTxA1tKDZRDfOy23KVAv7BAxiG60u_OhKP29nxaDSnFysxlZ0EgH1Z1dKp_9Zo9GnsROJ_qRSFvGBMGUKLZqm1g4HusVBRop0Ds0PTe6OK-rs09ruXBAahmJkiKt4XPzRJm4qPpnatyfg5HEgztY3SAQHp6iSx9lHIVEuXL7pNAjwWaraZIJSmY4FF-7fdO5a0Jj64zs9AmZuJaCjE-mxxRgDi5Myv_8MRg7CidjPYZ7BxYT61qFpV1AIYDrsng43CSi4ZVhrl-9lZZkWipX_qQgEB5mMCOoeEUczBcqSBWGZvqYQGhEmTUsyvn5lakWVMAlpYTnxLzNC10OwExmdW_TdClSW22qZgIPOR1hb1JNmVPwKYWXSBt_U_pdbPYwpwpsBq2JdTJmdQI1_IF_UYCWWBAEVNn_Rt0XrWzgsxEH1CUXnRrR4coC64A0lfYmi7OBMHmaK-Qm6mgbOpxGy1KRQ_wRZM2bwtwGWwcadAinL0TQcRzipNKICt8IkUuRfZILEcfzi_yVxn_B6OwAwna0-sAQENRPgwQcuF17iR9mgoZMPU2aHoXSLEvqshH6afBK5fuYzJrD6OiR8iY4J7eJ8OJkL_a6zxpt0zhj3BYFUNr8fqYN8RaqYawgfLTlQlLTgd-GqGwPeonzTU5QB0ygmsD0EGDeChwjSAg_rbbpiO_9U6s75uG5b-BDr2ucturQl7XFOpoXwa839Cf1kPBzlgx1xAfhXOevlieSRyI9zVza5Up-a7d2K6VEuSSK4OhuDZ7lGDebIMXPg4VlSPv_cIF7wBBII0sBlmmoIW3AeVSGGTKMHDPE1XPqXHwtIbwKF1hD9qS7iK-GyJoaw20sHgLdLvxhb0RHgby3uF0iZDG0vuXF48g1zGAcfPqigFx2ZUIjhEzopIC2ZdF1ZBvrrdTlu4QQKx4LF-XKMbse1f9HL_UOXm4voC-2i7AH7JiU6IiFjgicobOORDJhADyN7kk5L598Lc67irJ2X-RiJ6XNFHMi1BetmrHsn2yv5oNV6xLcsMqS7GIDZtBhba3gcPFidNCgEGACpaGEga7_ykSxc1RhxFzhxC_dSDhOFu8BXb-5vJCV8KScQwKoLWEmm236AO8cFBEVn5hpE2m1b1IiHHOFkQUiYNT66AUTa7ldPiIJiJjDiSxVX9AxUDrp5bAHML47qBBJeEMQDFDWl4HKWlyXV_5CERlt1sxjlXq5beQzRckI7dIUbf3P1JehPO6-nJSCqrhlHftERyDgz3mK3FceNdte_5u_EgbZZOo7Dqun2wU7ysUE_zsx84sifIxsjVK2cegQN6FsumFW2fZIJV6ttlCd9DN6DWPiyQHEJ4d6aXLosBXgJ_WjYo6HO2BeWvxii_voQ2K47nE-6v3hcieR5umDbb9GhSZEchgk8zNOFsQRGOtWHvV27qJYojX5OJWQjnyEEAUbfeJ1f1jY0Vqsg0v66pzmvOWMvsw_bs6wE1xldY46hBva20GRIR8ZA7RZeBVnLB9Sexfc3ZVvl7RypOrtXexseZWHfplBs6KsPev4-BkLoHreI6N1n4pi0ltSaDXJkBAgIOnozhl4s1ux44BpY0sPpuPtnT-2f8KqUSfnBlgwIoBWrNMaPzBBWZn4k1IZ4JRSbGpMtOlb8FTaiK778bU-5YdCYTL8xQM3w_4eecIpCVGqEUStLQ1PN_AWSNOwuZwQTYu59TJXfY20N5tPLQ1oiKEq0gwqyvnLQPa-4BUViHTMIewSm5O55rZ-FHfHjN-AZv3aOrV_ivwMuEfa9shVrSJHnA8t42-hLotH3RTlCoQcey7r2bAtMbLqlqKy7Y-kECVXCrbi1AGDHRrASqAL9uqHtiGmkpoZKwJtAjknSfeUkZIAuxmQ53disAyn56ojqDbYO3t8AF-i176odpqBb4rtHYw6E-vobB57Rn-3J0fcMYW2-hFeqPcDEbYsJJCYtBA1y8vlOkUjbRdweR3OzobLsjZNvzzX3u-7cSOJh21jUv_I2imnEVrqJ73sQzS5yCQciKyhZ4fvS2FcNU2UHClDSgsrCg8kzp_ZLuAXmycw-KrYCaZe5LlC-4YkmVj5HbAQVfFbG8Owcleu4FKcfP84VT04p8yyqK9EyfnndL1DsYOATCWTNcFIpY-DZS2oZS_4FEVPVtBc_kavxq0GVK6T4y1IRZDnCyLAhXk-HvWapk4uCrK8Zx7c_AH6mmEdft0YECghFSgdkIGc7IbdpzxNjErR3334JaxuxSVCR8ciHJ71uPWEAJDaQauyPTJBMef8mCeF5oJ65BOw4cDW907f7x1qecx_zTvxTRvhNEoY5glkqYsPPcKa-peSNAp4wLXyYBBTF3VtTIEWB6X7moNOa_np_wDX4cW6bZdaQKVuBpzlZ8P-L-hHS63H3MSibafkqkgIMHR7T_EkCBACLIFqbcvjBf4ZfQm0ytgM0iSW-kQs02IstztE4l1lWagSfqpwWXgQLlv0z0_M_azQiGBrk3aQOeMjJlFEk76zjk4sPS0s8qP6vBnfCIOMHEM7J9ZX17BkRvcHcfkB57S97fdIvJ6Po-b6IP5upXasIFOaxZUlwDfrrcV1xiH-Egijk_PWY3yAEFTYNrnmnx2XC8Wu5VSYMHafE47CvaDIbX2mRjZpECPV5KKh9CH5haRa6q_jShM31COo1e8Zwnw-SpCxouQdqps1AmmnxyOqR3pwlrWldNhTbLo1gGR0t6VajxQgqYkbVBAWnOkk9Ga0b6tHgZy-SysFORQlBQYpqldaVLqoAKJ4s3JG5GqRlIV2EROuuRDcF5hlCAmDouXixNeRV8cAXyRvm_COUOCEUEAlz_f2isffManR2t5vZpui3iKrSbi1dvwQDISGhoD24ws8_JeVmGT4tHlrUAcSoIcVRDeicuV88filJWFaWqHZQx0kRjG4hYx0Z7wO5y8ifwBYu5SCZIpVptwPH5kZaErk3w5p9obRUiUVt4Nd0hERZOuyqEP2VCaAEA2sqRhrAYnPO6CBYweIw1XuZDzznh9LnVJxF_JSibt5K8LcR1DN9Av1Vyd1VvREwDZ1VlRwQSqfkgE9u5tlbPLyh0oGfKjnBIAkgEr78sZwT7tG5grn7INPqAxZ_s8eaBArf6h-pgNE7u_8v6KtmPkEYvu6N1fRYaZRQbDKkoyjrnA39O9AEqqAn7N9d5fPj6AmKKGkIG9VBOZpUmZ_HwlFL43KU_OvH8Ih20C6Rzuqs4tfvaQNIei33IWOgvVVNAwQVdS76TgK1epNca7ojRDLjTm9apjNEGM-9HUxI6X02WyflxBcJC-U-XIV292yZQlUOKsz_-p1roX6qJ88ZSzdNVBkXyeqWWEBxS_sV1G24waGmfu4p7vrq6LGAgBVYJ_YEFUYCuqctS7IkNEoPS6zoBXAAWoS7CPtgnM0wcG4obl1Nw7z3fE34SsuTEdqcwaaSAR8bjkiTg7l-T-my9QX9iEKhKs42CFc7oMmNQgqmk7p7QjC5hE_NP56ygXTxHVoTBAS2aqL-7vyDnIWNzyGB4x3l_na9B5Nrt6HvdVLI6IkfnhH-xeIc8FofRmAboc0FOxN8UoFSg4ZMCGNsCDJsvY02crol-RRSZWTzi5pGXJAvqqBYtY5lS0MlJyzHKRWaHgQZ37VEqz6vhc41otZ1P9T1xQtp9ktg_0mGtytHIcjvdTfzGC-gOX_7ssuSeXx30aMFE0zLDmENrwQH57W7IVQ6D9V6Z0bbhd1biSjbeBups0ZncczeEUOqLhOosOPytDAEAD6lrEIjl0S0ehopDbp8tXPKtH49jDRLoTon4CMhJn3vKXevlW2Ei8Hpz84CmUBpCnGzXq_6oN1PTrbCKPlzu7Q0dojK81cxNWPkBtbeVEP7ZKVzYA0YjduC29_hb3aRBBVLwHcHpV6_YxIJm_r7mKsI-OAH_Df8wJ1tIQ6Ik5EkjFk7DBUPqaSLdo6agd80jAcWlEP2JwhChzIzgE29jNzTN59MpjTZ3y-YKF2bQuNhgnKT4Ut0aiRVSAdrq5izrwQGAIEO58d0NqNCRix3twBPQWJwgL0-ti38dmZTGkrv3-9HRnudVlBmuR1Gd671mLUUSTfdrEHWg77-sfE3mtHs0XKkhL40qha5rZEbTt_gGsGJBtJzcreOXpflB37fivwEGUjoeoQGpIffFQWboZJ8ckC6GBhVFJzTzG-kUErhHY3D6yLDtcx3FN93ExpQ2azzD79Tbm4e-M4taKYGvTUGOW58exVvFnGShqCQ-mq3RdvnGT5GdcEhgSZW0lFuop9rBAPVAwg-UrY3H60kt_CyblfrcNLaquWHVW_siKpZd4MpVhpiT10MY-l-3k3i2pBqwgYnnNrHceFyZHmMsP7tyJuRRqEYJqeilMqoSEX05srt0cPRG7eaBFAWXK7G18E4iAQJsyVd-gd6HaXX-iMcjJ174FXOwaN4fs6g0_Eilj5mmuZFHieo1-9vVozXFiVxD7oPEQOjRN9cDnIPYht0n51Aofc9g0e0pecWX5B-4M4it2NU1ISY9pE7v7TVF5SVxIIEEOjpGRBvvyK0VexUg8IlmqbIEh0TlKcR_2ft5eCRuQeqDZVWyEhI9F2Ls1zHYayoF1WVbQYuThlA-4VuvkmeQZi2rAmgYAglSA78cON4eF57zgiKbCxK34YpTp3pTdUaBBm0xtO0si7HcS6n8RyDk7NfqAz-YDVPI0C3vXR5RIoD-Udf8HdVNFeqnArpIJcAsxmjpFNTZYoKhvznVKKrNbnkgxsWdgL1AY9LWymzprhEI8yWfaoq2I1R4YfrpTNKQAQBfMp_qSu1APjHtkcSmSkL_974eBnRguSMTxCPqxjUqkjU5nEECgYPZUrsao_ZGVIXuC38Zd9jCMgKSUGFY2OTVdgXmgjwg-jOgUSGHKKbB1HG8qj3TwE3hMVg4_LwLzcEGDDZ1Nz0q4m60FR-ZnJ5LjuIqirQMtcWti_kUdAtYyg5n3ysBYuYJQVb-jucR3kGAsVxobMqWrdAU0GVkzovJP2nRBuDPEBUDxKXZO-iHWbMkvaTYTruz329Aj6dr7GwBAmqbBghhnKBwQpRDHyId-fXfsQCVrgshT0CoOU-JrvdE05QSyr3L2HXp9tS17MlIhFzL0uLQ0XsdaKbrSwJ1IyL6izKv734ivfSlzLiKilkgZ-3IBr_RLz4Mr4e1alxSAQWVQTM-l9oegft7X09QqSAZ1ztFmLzKboSTu6_O0zOPdXQ2sGoqQqJNWg_4kfWm6sU1bz1oklYBaLEV6lGph9GSxwf0DG_j3rzAtBqxhZkCeBtk6pur53xb8J4VE-q4lAEGD-SWIjZZPMJtRrtcarK5efjYC5tOIrogQmmqHWzPvz0D_yKRnVSWA6Rh1QeOX8wBrq4jDk6eDJJwMM28HVu5oLpi6s6hJsU-EWOxXiA_j-ygxhEtrgJkAZx8EMopjFwBAqjRlNs1IvCbpznbOgEk1ofh4XpoMTIZqQmf-VPsXirWyJ_qOvDzI1x-967N_tvrRlbL-oC1FaOlQXlnOxc18arrQQ8aJ4RaVr06l4zwkQGsOyz8QfenD5TfDCV2_qhxgQEqsknmhc-psCwkg5pjreAbvB0PouH9TY4n5jj1qvhnC-P68hZZO6lIAsADTzrVLEQRD_3I5arUAqxAzm6Gm3ghES-QUyiPzifIp-jOLAKi4a-m4iTLo2Ob154gjthAngEA58zYmlt2a9vd8-_FrgaQAvLNv2i5Uhm8_4hQlN-2av582RelN-B6AUuIh_SMk4yDb1MVXOsdOOm2akMayZT035kldKtXcYasur8jOUkE7W5hL2esSDhB6eYOz1o99aCBA67QQmnbvpmvKCDiS97rfaCZqr4U0DCy9e0ylMcBqDlX-K0IxhDNhiiqRFMYbatqBGCGcwNAc7B6c1YYar6fVkqCA7jYFZZyYt3yCND6JkM2BTfpAiakknT03vY2P6-WgQOGRCN4mQttSdsajcySvOyW2ohNP_nwEHD2lEjzsgOI8jjh7WaJf8-8Oh-X_zNgnMJUfvMgwVfHFNOIAPX_zj1y2tVJMpAnD9eSUBsSG90ghPI0hS-IKPnxppjsWrMD28EGPCKVlP22usiIj8Y2sZeGKCyX7xm4GCI3TBDqI_tZ0MNZdLVzIv3CbQND9H7xyoCB_CIE6G9nQMfirWanfFwhgOKxWDUL5YjAPnn_FeT4J601JO3Bf3gNCbMSzAPwdkMBAkeNIIko2D1zcVyxiluQEsv88fDOxLgV3MjuXVG6mmc34E8bZUJwz3UrUBl91CMEAzfEPwxc_AEH9oaPJN_hANs5rwQMi8TIHPwETYV_VYIJKKon9CO3xKTZym0GJlrxgQVr2UhK2y7r_jnJgPromEPEYZTwqWC2kcXNJ41WLgrW9WQKa8VBSDTd7SQmj3r35oRVTI6U_3_d0OOqMHR8gitL93r9-xmIx9WljoJwuyPoS7BMjQssv_hK7MFXR8tXeIEBBxHxkVMUNPn5q_TWsjtcVYbQ8iGJCFZHBXkDljFQXnvvktOr9xx28qN-6fgNDmmA6KU9lKSXqypZ10DUL0Y-BWyubg0h3xLirUdxXHiZQnkxp5_NdWwRc_0GO1ioJNHBBjGRtlYTJlAMKux4uXiI1gSDuJ-bBUO43BAg012ryQxUsaYowT7Db_j0skDXMfO_hJu6tgnJF-oGjdH0OdHkoOdWsOZ3vINMY7U0EfYxqyT5fjT4BV54DKY0axWEpB8ywQMTD8k5Arj-DQCo-RziCOE3ISGh8ocX5AB8CX9zK15cZ1VjvLa1H_pS_X-pNpWqdQZB7mdFK6ZTWwC2EOXwnaghzLTaFWr39TLB7fSEETalvxPelrwtW6RzwUeXDSGboYEDBopTOJ4gGFGy3kj03mOE6eqZ8vKCwGE44S63NIgJWy6M5m3fgYJC-k1rH5CvWDFAlMtqrarDZbZi_R3ENGATY222Tun66K4cVEi1ytWm_566aJD8G_yvO5v_K829cE4"},"purposes":["assertionMethod","authentication","keyAgreement","capabilityInvocation","capabilityDelegation"]} - "#; - - const TEST_DOCUMENT_IPFS_KEY: &str = r##" - { - "@context" : [ - "https://www.w3.org/ns/did/v1", - { - "@base" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" - } - ], - "assertionMethod" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "authentication" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "capabilityDelegation" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "capabilityInvocation" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "id" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", - "keyAgreement" : [ - "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84" - ], - "service" : [ - { - "id" : "#trustchain-controller-proof", - "type" : "TrustchainProofService", - "serviceEndpoint" : { - "proofValue" : "eyJhbGciOiJFUzI1NksifQ.IkVpQmNiTkRRcjZZNHNzZGc5QXo4eC1qNy1yS1FuNWk5T2Q2S3BjZ2c0RU1KOXci.Nii8p38DtzyurmPHO9sV2JLSH7-Pv-dCKQ0Y-H34rplwhhwca2nSra4ZofcUsHCG6u1oKJ0x4AmMUD2_3UIhRA", - "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ" - } - }, - { - "id": "RSSPublicKey", - "type": "IPFSKey", - "serviceEndpoint": "QmNqvEP6qmRLQ6aGz5G8fKTV7BcaBoq8gdCD5xY8PZ33aD" - } - ], - "verificationMethod" : [ - { - "controller" : "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ", - "id" : "#V8jt_0c-aFlq40Uti2R_WiquxuzxyB8kn1cfWmXIU84", - "publicKeyJwk" : { - "crv" : "secp256k1", - "kty" : "EC", - "x" : "RbIj1Y4jeqkn0cizEfxHZidD-GQouFmAtE6YCpxFjpg", - "y" : "ZcbgNp3hrfp3cujZFKqgFS0uFGOn2Rk16Y9nOv0h15s" - }, - "type" : "JsonWebSignature2020" - } - ] - } - "##; }