1use crate::TRUSTCHAIN_PROOF_SERVICE_ID_VALUE;
3use async_trait::async_trait;
4use did_method_key::DIDKey;
5use serde_json::Value;
6use ssi::did::{Document, Service, ServiceEndpoint};
7use ssi::did_resolve::{
8 DIDResolver, DocumentMetadata, Metadata, ResolutionInputMetadata, ResolutionMetadata,
9};
10use ssi::one_or_many::OneOrMany;
11use std::collections::HashMap;
12use thiserror::Error;
13
14#[derive(Error, Debug)]
16pub enum ResolverError {
17 #[error("Controller is already present in DID document.")]
19 ControllerAlreadyPresent,
20 #[error("Failed to convert to Trustchain document and metadata: {0}")]
22 FailedToConvertToTrustchain(String),
23 #[error("Multiple Trustchain proof service entries are present.")]
25 MultipleTrustchainProofService,
26 #[error("No Trustchain proof service is present.")]
28 NoTrustchainProofService,
29 #[error("Cannot connect to sidetree server.")]
31 ConnectionFailure,
32 #[error("DID: {0} does not exist.")]
34 NonExistentDID(String),
35 #[error("DID: {0} is not found.")]
37 DIDNotFound(String),
38 #[error("Resolver error with resolution metadata.")]
40 FailureWithMetadata(ResolutionMetadata),
41}
42
43pub type ResolverResult = Result<
45 (
46 ResolutionMetadata,
47 Option<Document>,
48 Option<DocumentMetadata>,
49 ),
50 ResolverError,
51>;
52
53fn add_controller(mut doc: Document, controller_did: &str) -> Result<Document, ResolverError> {
57 if doc.controller.is_some() {
59 return Err(ResolverError::ControllerAlreadyPresent);
60 }
61
62 doc.controller = Some(OneOrMany::One(controller_did.to_string()));
64
65 Ok(doc)
67}
68
69fn get_proof_idx(doc: &Document) -> Result<usize, ResolverError> {
71 let mut idxs: Vec<usize> = Vec::new();
72 let fragment = TRUSTCHAIN_PROOF_SERVICE_ID_VALUE;
73 for (idx, service) in doc.service.iter().flatten().enumerate() {
74 if let [service_fragment, _] = service.id.rsplitn(2, '#').collect::<Vec<&str>>().as_slice()
75 {
76 if service_fragment == &fragment {
77 idxs.push(idx);
78 }
79 }
80 }
81 match idxs.len() {
82 0 => Err(ResolverError::NoTrustchainProofService),
83 1 => Ok(idxs[0]),
84 _ => Err(ResolverError::MultipleTrustchainProofService),
85 }
86}
87
88fn get_proof_service(doc: &Document) -> Result<&Service, ResolverError> {
90 let idxs = get_proof_idx(doc);
92 match idxs {
93 Ok(idx) => Ok(&doc.service.as_ref().unwrap()[idx]),
94 Err(e) => Err(e),
95 }
96}
97
98fn get_from_proof_service<'a>(proof_service: &'a Service, key: &str) -> Option<&'a String> {
100 let value: Option<&String> = match proof_service.service_endpoint.as_ref() {
102 Some(OneOrMany::One(ServiceEndpoint::Map(Value::Object(v)))) => match &v[key] {
103 Value::String(s) => Some(s),
104 _ => None,
105 },
106 _ => None,
107 };
108 value
109}
110
111fn add_proof(doc: &Document, mut doc_meta: DocumentMetadata) -> DocumentMetadata {
113 let proof_service = get_proof_service(doc);
115
116 if let Ok(proof_service) = proof_service {
118 let proof_value = get_from_proof_service(proof_service, "proofValue");
120 let controller = get_from_proof_service(proof_service, "controller");
121 if let (Some(property_set), Some(proof_value), Some(controller)) =
123 (doc_meta.property_set.as_mut(), proof_value, controller)
124 {
125 let mut proof_hash_map: HashMap<String, Metadata> = HashMap::new();
127 proof_hash_map.insert(String::from("id"), Metadata::String(controller.to_owned()));
128 proof_hash_map.insert(
129 String::from("type"),
130 Metadata::String("JsonWebSignature2020".to_string()),
131 );
132 proof_hash_map.insert(
133 String::from("proofValue"),
134 Metadata::String(proof_value.to_owned()),
135 );
136
137 property_set.insert(String::from("proof"), Metadata::Map(proof_hash_map));
139 return doc_meta;
140 }
141 }
142 doc_meta
144}
145
146fn remove_proof_service(mut doc: Document) -> Document {
148 if doc.service.is_some() {
149 let idx_result = get_proof_idx(&doc);
150 if let Ok(idx) = idx_result {
151 let services = doc.service.as_mut().unwrap();
152 services.remove(idx);
153 if services.is_empty() {
154 doc.service = None;
155 }
156 }
157 }
158 doc
159}
160
161fn transform_doc(doc: &Document, controller_did: &str) -> Document {
163 let doc_clone = doc.clone();
165
166 let doc_clone =
168 add_controller(doc_clone, controller_did).expect("Controller already present in document.");
169
170 remove_proof_service(doc_clone)
172}
173
174fn transform_doc_metadata(doc: &Document, doc_meta: DocumentMetadata) -> DocumentMetadata {
176 add_proof(doc, doc_meta)
178}
179
180fn transform_as_result(
182 sidetree_res_meta: ResolutionMetadata,
183 sidetree_doc: Document,
184 sidetree_doc_meta: DocumentMetadata,
185) -> Result<(ResolutionMetadata, Document, DocumentMetadata), ResolverError> {
186 let service = get_proof_service(&sidetree_doc);
188
189 if let Err(ResolverError::MultipleTrustchainProofService) = service {
191 return Err(ResolverError::MultipleTrustchainProofService);
192 };
193
194 if let Ok(service) = service {
195 let controller_did = get_from_proof_service(service, "controller");
196
197 let doc = transform_doc(&sidetree_doc, controller_did.unwrap().as_str());
199
200 let doc_meta = transform_doc_metadata(&sidetree_doc, sidetree_doc_meta);
202
203 let res_meta = sidetree_res_meta;
205
206 Ok((res_meta, doc, doc_meta))
208 } else {
209 Ok((sidetree_res_meta, sidetree_doc, sidetree_doc_meta))
211 }
212}
213
214#[async_trait]
216pub trait TrustchainResolver: DIDResolver + AsDIDResolver {
217 fn wrapped_resolver(&self) -> &dyn DIDResolver;
220
221 fn transform(
223 &self,
224 (res_meta, doc, doc_meta): (
225 ResolutionMetadata,
226 Option<Document>,
227 Option<DocumentMetadata>,
228 ),
229 ) -> (
230 ResolutionMetadata,
231 Option<Document>,
232 Option<DocumentMetadata>,
233 ) {
234 if let (Some(did_doc), Some(did_doc_meta)) = (doc, doc_meta) {
236 let tc_result = transform_as_result(res_meta, did_doc, did_doc_meta);
238 match tc_result {
239 Ok((tc_res_meta, tc_doc, tc_doc_meta)) => {
241 (tc_res_meta, Some(tc_doc), Some(tc_doc_meta))
242 }
243 Err(ResolverError::FailedToConvertToTrustchain(err)) => {
245 let res_meta = ResolutionMetadata {
246 error: Some(err.to_string()),
247 content_type: None,
248 property_set: None,
249 };
250 (res_meta, None, None)
251 }
252 Err(ResolverError::MultipleTrustchainProofService) => {
253 let res_meta = ResolutionMetadata {
254 error: Some("Found multiple Trustchain proof service entries.".to_string()),
255 content_type: None,
256 property_set: None,
257 };
258 (res_meta, None, None)
259 }
260 Err(err) => {
261 let res_meta = ResolutionMetadata {
262 error: Some(err.to_string()),
263 content_type: None,
264 property_set: None,
265 };
266 (res_meta, None, None)
267 }
268 }
269 } else {
270 (res_meta, None, None)
272 }
273 }
274
275 async fn resolve_as_result(&self, did: &str) -> ResolverResult {
278 let (did_res_meta, did_doc, did_doc_meta) =
280 self.resolve(did, &ResolutionInputMetadata::default()).await;
281
282 if let Some(did_res_meta_error) = &did_res_meta.error {
284 if did_res_meta_error
285 .starts_with("Error sending HTTP request: error sending request for url")
286 {
287 Err(ResolverError::ConnectionFailure)
288 } else if did_res_meta_error == "invalidDid" {
289 Err(ResolverError::NonExistentDID(did.to_string()))
290 } else if did_res_meta_error == "notFound" {
291 Err(ResolverError::DIDNotFound(did.to_string()))
292 } else if did_res_meta_error.contains("Failed to convert to Trustchain") {
293 Err(ResolverError::FailedToConvertToTrustchain(
294 did_res_meta_error
295 .to_owned()
296 .rsplit(':')
297 .next()
298 .unwrap_or("")
299 .to_owned(),
300 ))
301 } else if did_res_meta_error == "Multiple Trustchain proof service entries are present."
302 {
303 Err(ResolverError::MultipleTrustchainProofService)
304 } else {
305 eprintln!("Unhandled error message: {}", did_res_meta_error);
306 let eof_err_msg = "Error parsing resolution response: EOF while parsing a value at line 1 column 0";
307 if did_res_meta_error == eof_err_msg {
308 eprintln!(
309 "HINT: If using HTTP for resolution, ensure a valid client is in use."
310 );
311 }
312 panic!();
313 }
314 } else {
315 Ok((did_res_meta, did_doc, did_doc_meta))
316 }
317 }
318
319 async fn trustchain_resolve(
320 &self,
321 did: &str,
322 input_metadata: &ResolutionInputMetadata,
323 ) -> (
324 ResolutionMetadata,
325 Option<Document>,
326 Option<DocumentMetadata>,
327 ) {
328 if did.starts_with("did:key:") {
330 let did_key_resolver = DIDKey;
331 return did_key_resolver
332 .resolve(did, &ResolutionInputMetadata::default())
333 .await;
334 }
335
336 let resolved = self.wrapped_resolver().resolve(did, input_metadata).await;
337
338 let transformed = self.transform(resolved);
341 self.extended_transform(transformed).await
342 }
343
344 async fn extended_transform(
347 &self,
348 (res_meta, doc, doc_meta): (
349 ResolutionMetadata,
350 Option<Document>,
351 Option<DocumentMetadata>,
352 ),
353 ) -> (
354 ResolutionMetadata,
355 Option<Document>,
356 Option<DocumentMetadata>,
357 ) {
358 (res_meta, doc, doc_meta)
359 }
360}
361
362pub trait AsDIDResolver {
364 fn as_did_resolver(&self) -> &dyn DIDResolver;
365}
366
367impl<T: DIDResolver> AsDIDResolver for T {
368 fn as_did_resolver(&self) -> &dyn DIDResolver {
369 self
370 }
371}
372
373#[cfg(test)]
374mod tests {
375 use super::*;
376 use crate::data::{
377 TEST_SIDETREE_DOCUMENT, TEST_SIDETREE_DOCUMENT_METADATA,
378 TEST_SIDETREE_DOCUMENT_MULTIPLE_PROOF, TEST_SIDETREE_DOCUMENT_SERVICE_AND_PROOF,
379 TEST_SIDETREE_DOCUMENT_SERVICE_NOT_PROOF, TEST_SIDETREE_DOCUMENT_WITH_CONTROLLER,
380 TEST_TRUSTCHAIN_DOCUMENT, TEST_TRUSTCHAIN_DOCUMENT_METADATA,
381 };
382 use crate::utils::canonicalize;
383
384 #[test]
385 fn test_add_controller() {
386 let controller_did = "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9YP";
389
390 let did_doc =
392 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
393
394 assert!(did_doc.controller.is_none());
396
397 let result =
399 add_controller(did_doc, controller_did).expect("Different Controller already present.");
400
401 assert!(result.controller.is_some());
403 assert_eq!(
405 result.controller,
406 Some(OneOrMany::One(String::from(controller_did)))
407 );
408
409 let expected = Document::from_json(TEST_SIDETREE_DOCUMENT_WITH_CONTROLLER)
411 .expect("Document failed to load.");
412
413 assert_eq!(result, expected);
415 }
416
417 #[test]
418 fn test_add_controller_fail() {
419 let controller_did = "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9YP";
422
423 let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT_WITH_CONTROLLER)
425 .expect("Document failed to load.");
426
427 assert!(did_doc.controller.is_some());
429
430 let result = add_controller(did_doc, controller_did);
434
435 assert!(matches!(
437 result,
438 Err(ResolverError::ControllerAlreadyPresent)
439 ));
440 }
441
442 #[test]
443 fn test_remove_proof_service() {
444 let did_doc =
448 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
449
450 assert!(did_doc.service.is_some());
452
453 let did_doc_no_proof_service = remove_proof_service(did_doc);
455
456 assert!(did_doc_no_proof_service.service.is_none());
458 }
459
460 #[test]
461 fn test_get_proof_service() {
462 let did_doc =
466 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
467
468 assert_eq!(did_doc.service.as_ref().unwrap().len(), 1_usize);
470
471 let proof_service = get_proof_service(&did_doc).unwrap();
473
474 assert_eq!(proof_service.id, format!("#trustchain-controller-proof"));
476 assert_eq!(
477 proof_service.type_,
478 OneOrMany::One(String::from("TrustchainProofService"))
479 );
480 }
481
482 #[test]
483 fn test_get_proof_service_only() {
484 let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT_SERVICE_AND_PROOF)
488 .expect("Document failed to load.");
489
490 assert_eq!(did_doc.service.as_ref().unwrap().len(), 2_usize);
492
493 let proof_service = get_proof_service(&did_doc).unwrap();
495
496 assert_eq!(proof_service.id, format!("#trustchain-controller-proof"));
498 assert_eq!(
499 proof_service.type_,
500 OneOrMany::One(String::from("TrustchainProofService"))
501 );
502 }
503
504 #[test]
505 fn test_get_proof_service_fail_multiple_proof_services() {
506 let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT_MULTIPLE_PROOF)
510 .expect("Document failed to load.");
511
512 assert_eq!(did_doc.service.as_ref().unwrap().len(), 2_usize);
514
515 let result = get_proof_service(&did_doc);
516
517 assert!(matches!(
519 result,
520 Err(ResolverError::MultipleTrustchainProofService)
521 ));
522 }
523
524 #[test]
525 fn test_get_proof_service_fail_no_proof_services() {
526 let did_doc = Document::from_json(TEST_SIDETREE_DOCUMENT_SERVICE_NOT_PROOF)
530 .expect("Document failed to load.");
531
532 assert!(did_doc.service.is_some());
534
535 let result = get_proof_service(&did_doc);
536
537 assert!(matches!(
539 result,
540 Err(ResolverError::NoTrustchainProofService)
541 ));
542 }
543
544 #[test]
545 fn test_get_proof_service_fail_no_services() {
546 let did_doc =
550 Document::from_json(TEST_TRUSTCHAIN_DOCUMENT).expect("Document failed to load.");
551
552 assert!(did_doc.service.is_none());
554
555 let result = get_proof_service(&did_doc);
556
557 assert!(matches!(
559 result,
560 Err(ResolverError::NoTrustchainProofService)
561 ));
562 }
563
564 #[test]
565 fn test_get_from_proof_service() {
566 let did_doc =
570 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
571
572 let service = get_proof_service(&did_doc).unwrap();
574
575 let controller = get_from_proof_service(service, "controller").unwrap();
577
578 assert_eq!(
580 controller,
581 "did:ion:test:EiCBr7qGDecjkR2yUBhn3aNJPUR3TSEOlkpNcL0Q5Au9ZQ"
582 )
583 }
584
585 #[test]
586 fn test_add_proof() {
587 let sidetree_doc =
591 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load doc.");
592
593 let sidetree_meta: DocumentMetadata =
595 serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA).expect("Failed to load metadata");
596
597 let expected_tc_meta: DocumentMetadata =
599 serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)
600 .expect("Failed to load metadata");
601 let expected_tc_meta =
602 canonicalize(&expected_tc_meta).expect("Cannot add proof and canonicalize.");
603
604 let actual_tc_meta = canonicalize(&add_proof(&sidetree_doc, sidetree_meta))
606 .expect("Cannot add proof and canonicalize.");
607
608 assert_eq!(expected_tc_meta, actual_tc_meta);
610 }
611
612 #[test]
613 fn test_transform_doc_metadata() {
614 let did_doc =
620 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load doc.");
621
622 let sidetree_meta: DocumentMetadata =
624 serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA).expect("Failed to load metadata");
625
626 let actual = transform_doc_metadata(&did_doc, sidetree_meta);
628
629 let canon_actual_meta = canonicalize(&actual).expect("Cannot add proof and canonicalize.");
631
632 let tc_meta: DocumentMetadata = serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)
633 .expect("Failed to load metadata");
634 let canon_tc_meta = canonicalize(&tc_meta).expect("Cannot add proof and canonicalize.");
635
636 assert_eq!(canon_tc_meta, canon_actual_meta);
637 }
638
639 #[test]
640 fn test_transform_doc() {
641 let did_doc =
645 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
646
647 let proof_service = get_proof_service(&did_doc).unwrap();
649 let controller = get_from_proof_service(proof_service, "controller").unwrap();
650
651 let actual = transform_doc(&did_doc, controller.as_str());
653
654 let canon_actual_doc = canonicalize(&actual).expect("Failed to canonicalize.");
656
657 let tc_doc =
658 Document::from_json(TEST_TRUSTCHAIN_DOCUMENT).expect("Document failed to load.");
659 let canon_tc_doc = canonicalize(&tc_doc).expect("Failed to canonicalize.");
660
661 assert_eq!(canon_tc_doc, canon_actual_doc);
662 }
663
664 #[test]
665 fn test_transform_as_result() {
666 let input_doc =
670 Document::from_json(TEST_SIDETREE_DOCUMENT).expect("Document failed to load.");
671 let expected_output_doc =
672 Document::from_json(TEST_TRUSTCHAIN_DOCUMENT).expect("Document failed to load.");
673 let input_doc_meta: DocumentMetadata =
674 serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA)
675 .expect("Document failed to load.");
676 let expected_output_doc_meta: DocumentMetadata =
677 serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)
678 .expect("Document failed to load.");
679 let input_res_meta = ResolutionMetadata {
680 error: None,
681 content_type: None,
682 property_set: None,
683 };
684 let expected_output_res_meta = ResolutionMetadata {
685 error: None,
686 content_type: None,
687 property_set: None,
688 };
689
690 let output = transform_as_result(input_res_meta, input_doc, input_doc_meta);
692
693 if let Ok((actual_output_res_meta, actual_output_doc, actual_output_doc_meta)) = output {
695 assert_eq!(
697 canonicalize(&expected_output_res_meta).unwrap(),
698 canonicalize(&actual_output_res_meta).unwrap()
699 );
700 assert_eq!(expected_output_doc, actual_output_doc);
702 assert_eq!(
704 canonicalize(&expected_output_doc_meta).unwrap(),
705 canonicalize(&actual_output_doc_meta).unwrap()
706 );
707 } else {
708 panic!()
710 }
711 }
712
713 #[test]
714 fn transform_as_result_with_multiple_proof_services() {
715 let input_doc = Document::from_json(TEST_SIDETREE_DOCUMENT_MULTIPLE_PROOF)
720 .expect("Document failed to load.");
721 let input_doc_meta: DocumentMetadata =
722 serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA)
723 .expect("Document failed to load.");
724 let input_res_meta = ResolutionMetadata {
725 error: None,
726 content_type: None,
727 property_set: None,
728 };
729
730 let output = transform_as_result(input_res_meta, input_doc, input_doc_meta);
732
733 assert!(matches!(
735 output,
736 Err(ResolverError::MultipleTrustchainProofService)
737 ));
738 }
739}