1use crate::{
8 DIDWebVHError, DIDWebVHState, Signer, ensure_object_mut,
9 log_entry::{LogEntry, LogEntryMethods},
10 log_entry_state::LogEntryState,
11 parameters::Parameters,
12 url::WebVHURL,
13 witness::{Witnesses, proofs::WitnessProofCollection},
14};
15use affinidi_data_integrity::{DataIntegrityProof, SignOptions};
16use affinidi_secrets_resolver::secrets::Secret;
17use ahash::HashMap;
18use serde_json::{Value, json};
19use std::sync::Arc;
20use url::Url;
21
22pub struct CreateDIDConfig<A: Signer = Secret, W: Signer = Secret> {
27 pub address: String,
29 pub authorization_keys: Vec<A>,
31 pub did_document: Value,
33 pub parameters: Parameters,
35 pub witness_secrets: HashMap<String, W>,
37 pub also_known_as_web: bool,
39 pub also_known_as_scid: bool,
41 pub version_time: Option<chrono::DateTime<chrono::FixedOffset>>,
46}
47
48pub struct CreateDIDConfigBuilder<A: Signer = Secret, W: Signer = Secret> {
71 address: Option<String>,
72 authorization_keys: Vec<A>,
73 did_document: Option<Value>,
74 parameters: Option<Parameters>,
75 witness_secrets: HashMap<String, W>,
76 also_known_as_web: bool,
77 also_known_as_scid: bool,
78 version_time: Option<chrono::DateTime<chrono::FixedOffset>>,
79}
80
81impl<A: Signer, W: Signer> CreateDIDConfigBuilder<A, W> {
82 fn new() -> Self {
83 Self {
84 address: None,
85 authorization_keys: Vec::new(),
86 did_document: None,
87 parameters: None,
88 witness_secrets: HashMap::default(),
89 also_known_as_web: false,
90 also_known_as_scid: false,
91 version_time: None,
92 }
93 }
94
95 pub fn address(mut self, address: impl Into<String>) -> Self {
97 self.address = Some(address.into());
98 self
99 }
100
101 pub fn authorization_key(mut self, key: A) -> Self {
103 self.authorization_keys.push(key);
104 self
105 }
106
107 pub fn authorization_keys(mut self, keys: Vec<A>) -> Self {
109 self.authorization_keys = keys;
110 self
111 }
112
113 pub fn did_document(mut self, doc: Value) -> Self {
115 self.did_document = Some(doc);
116 self
117 }
118
119 pub fn parameters(mut self, params: Parameters) -> Self {
121 self.parameters = Some(params);
122 self
123 }
124
125 pub fn witness_secret(mut self, did: impl Into<String>, secret: W) -> Self {
127 self.witness_secrets.insert(did.into(), secret);
128 self
129 }
130
131 pub fn witness_secrets(mut self, secrets: HashMap<String, W>) -> Self {
133 self.witness_secrets = secrets;
134 self
135 }
136
137 pub fn also_known_as_web(mut self, enabled: bool) -> Self {
139 self.also_known_as_web = enabled;
140 self
141 }
142
143 pub fn also_known_as_scid(mut self, enabled: bool) -> Self {
145 self.also_known_as_scid = enabled;
146 self
147 }
148
149 pub fn version_time(mut self, version_time: chrono::DateTime<chrono::FixedOffset>) -> Self {
151 self.version_time = Some(version_time);
152 self
153 }
154
155 pub fn build(self) -> Result<CreateDIDConfig<A, W>, DIDWebVHError> {
157 let address = self
158 .address
159 .ok_or_else(|| DIDWebVHError::DIDError("address is required".to_string()))?;
160 if self.authorization_keys.is_empty() {
161 return Err(DIDWebVHError::LogEntryError(
162 "At least one authorization key is required".to_string(),
163 ));
164 }
165 let did_document = self
166 .did_document
167 .ok_or_else(|| DIDWebVHError::DIDError("did_document is required".to_string()))?;
168 let parameters = self
169 .parameters
170 .ok_or_else(|| DIDWebVHError::ParametersError("parameters is required".to_string()))?;
171
172 match did_document.get("id") {
174 Some(Value::String(id)) => {
175 if !id.contains("{SCID}") && !id.contains("{DID}") {
177 return Err(DIDWebVHError::DIDError(
178 "DID document 'id' must contain a '{SCID}' or '{DID}' placeholder \
179 (e.g. \"did:webvh:{SCID}:example.com\" or \"{DID}\"). \
180 The placeholder is replaced with the actual identifier during creation."
181 .to_string(),
182 ));
183 }
184 }
185 Some(_) => {
186 return Err(DIDWebVHError::DIDError(
187 "DID document 'id' field must be a string".to_string(),
188 ));
189 }
190 None => {
191 return Err(DIDWebVHError::DIDError(
192 "DID document must have a top-level 'id' field".to_string(),
193 ));
194 }
195 }
196
197 Ok(CreateDIDConfig {
198 address,
199 authorization_keys: self.authorization_keys,
200 did_document,
201 parameters,
202 witness_secrets: self.witness_secrets,
203 also_known_as_web: self.also_known_as_web,
204 also_known_as_scid: self.also_known_as_scid,
205 version_time: self.version_time,
206 })
207 }
208}
209
210impl CreateDIDConfig {
211 pub fn builder() -> CreateDIDConfigBuilder {
215 CreateDIDConfigBuilder::new()
216 }
217}
218
219impl<A: Signer, W: Signer> CreateDIDConfig<A, W> {
220 pub fn builder_generic() -> CreateDIDConfigBuilder<A, W> {
222 CreateDIDConfigBuilder::new()
223 }
224}
225
226#[derive(Clone, Debug)]
228pub struct CreateDIDResult {
229 pub(crate) did: String,
231 pub(crate) log_entry: LogEntry,
233 pub(crate) witness_proofs: WitnessProofCollection,
235}
236
237impl CreateDIDResult {
238 pub fn did(&self) -> &str {
240 &self.did
241 }
242
243 pub fn log_entry(&self) -> &LogEntry {
245 &self.log_entry
246 }
247
248 pub fn witness_proofs(&self) -> &WitnessProofCollection {
250 &self.witness_proofs
251 }
252}
253
254fn validate_did_key_vm(vm: &str) -> Result<(), DIDWebVHError> {
256 if !vm.starts_with("did:key:") || !vm.contains('#') {
257 return Err(DIDWebVHError::LogEntryError(format!(
258 "Signer verification_method '{vm}' must be in 'did:key:{{mb}}#{{mb}}' format"
259 )));
260 }
261 Ok(())
262}
263
264pub async fn create_did<A: Signer, W: Signer>(
275 mut config: CreateDIDConfig<A, W>,
276) -> Result<CreateDIDResult, DIDWebVHError> {
277 let did_url = if config.address.starts_with("did:") {
279 WebVHURL::parse_did_url(&config.address)?
280 } else {
281 let url = Url::parse(&config.address).map_err(|e| {
282 DIDWebVHError::DIDError(format!("Invalid URL ({}): {e}", config.address))
283 })?;
284 WebVHURL::parse_url(&url)?
285 };
286
287 let webvh_did = did_url.to_string();
288
289 if config.also_known_as_web {
291 add_web_also_known_as(&mut config.did_document, &webvh_did)?;
292 }
293
294 if config.also_known_as_scid {
296 add_scid_also_known_as(&mut config.did_document, &webvh_did)?;
297 }
298
299 replace_did_placeholder(&mut config.did_document, &webvh_did);
300
301 for key in &config.authorization_keys {
303 validate_did_key_vm(key.verification_method())?;
304 }
305
306 let mut didwebvh = DIDWebVHState::default();
308 let signing_key = config.authorization_keys.first().ok_or_else(|| {
309 DIDWebVHError::LogEntryError("At least one authorization key is required".to_string())
310 })?;
311
312 let log_entry_state = didwebvh
313 .create_log_entry(
314 config.version_time,
315 &config.did_document,
316 &config.parameters,
317 signing_key,
318 )
319 .await?;
320
321 log_entry_state.log_entry.verify_log_entry(None, None)?;
323
324 let resolved_did =
326 if let Some(Value::String(id)) = log_entry_state.log_entry.get_state().get("id") {
327 id.clone()
328 } else {
329 webvh_did
330 };
331
332 let log_entry = log_entry_state.log_entry.clone();
334 let active_witnesses = log_entry_state.get_active_witnesses();
335
336 let mut witness_proofs = WitnessProofCollection::default();
338 sign_witness_proofs(
339 &mut witness_proofs,
340 log_entry_state,
341 &active_witnesses,
342 &config.witness_secrets,
343 )
344 .await?;
345
346 Ok(CreateDIDResult {
347 did: resolved_did,
348 log_entry,
349 witness_proofs,
350 })
351}
352
353fn replace_did_placeholder(did_document: &mut Value, did: &str) {
363 match did_document {
364 Value::Object(map) => {
365 for value in map.values_mut() {
366 replace_did_placeholder(value, did);
367 }
368 }
369 Value::Array(arr) => {
370 for value in arr.iter_mut() {
371 replace_did_placeholder(value, did);
372 }
373 }
374 Value::String(s) if s.contains("{DID}") => {
375 *s = s.replace("{DID}", did);
376 }
377 _ => {}
378 }
379}
380
381pub fn add_web_also_known_as(did_document: &mut Value, did: &str) -> Result<(), DIDWebVHError> {
386 let did_web_id = DIDWebVHState::convert_webvh_id_to_web_id(did);
387
388 let also_known_as = did_document.get_mut("alsoKnownAs");
389
390 let Some(also_known_as) = also_known_as else {
391 ensure_object_mut(did_document)?.insert(
393 "alsoKnownAs".to_string(),
394 Value::Array(vec![Value::String(did_web_id.to_string())]),
395 );
396 return Ok(());
397 };
398
399 let new_aliases = build_alias_list(also_known_as, &did_web_id)?;
400
401 ensure_object_mut(did_document)?.insert("alsoKnownAs".to_string(), Value::Array(new_aliases));
402
403 Ok(())
404}
405
406pub fn add_scid_also_known_as(did_document: &mut Value, did: &str) -> Result<(), DIDWebVHError> {
411 let did_scid_id = DIDWebVHState::convert_webvh_id_to_scid_id(did);
412
413 let also_known_as = did_document.get_mut("alsoKnownAs");
414
415 let Some(also_known_as) = also_known_as else {
416 ensure_object_mut(did_document)?.insert(
418 "alsoKnownAs".to_string(),
419 Value::Array(vec![Value::String(did_scid_id.to_string())]),
420 );
421 return Ok(());
422 };
423
424 let new_aliases = build_alias_list(also_known_as, &did_scid_id)?;
425
426 ensure_object_mut(did_document)?.insert("alsoKnownAs".to_string(), Value::Array(new_aliases));
427
428 Ok(())
429}
430
431fn build_alias_list(also_known_as: &Value, new_alias: &str) -> Result<Vec<Value>, DIDWebVHError> {
433 let mut new_aliases = vec![];
434 let mut already_exists = false;
435
436 if let Some(aliases) = also_known_as.as_array() {
437 for alias in aliases {
438 if let Some(alias_str) = alias.as_str() {
439 if alias_str == new_alias {
440 already_exists = true;
441 }
442 new_aliases.push(alias.clone());
443 }
444 }
445 } else {
446 return Err(DIDWebVHError::DIDError(
447 "alsoKnownAs is not an array".to_string(),
448 ));
449 }
450
451 if !already_exists {
452 new_aliases.push(Value::String(new_alias.to_string()));
453 }
454
455 Ok(new_aliases)
456}
457
458#[allow(
471 clippy::implicit_hasher,
472 reason = "didwebvh-rs uses ahash::HashMap uniformly; adding an S-generic \
473 is churn without a real interop need."
474)]
475pub async fn sign_witness_proofs<W: Signer>(
476 witness_proofs: &mut WitnessProofCollection,
477 log_entry: &LogEntryState,
478 witnesses: &Option<Arc<Witnesses>>,
479 witness_secrets: &HashMap<String, W>,
480) -> Result<bool, DIDWebVHError> {
481 let Some(witnesses) = witnesses else {
482 return Ok(false);
483 };
484
485 let (_, witness_nodes) = match &**witnesses {
486 Witnesses::Value {
487 threshold,
488 witnesses,
489 } => (threshold, witnesses),
490 _ => {
491 return Err(DIDWebVHError::WitnessProofError(
492 "No valid witness parameter config found".to_string(),
493 ));
494 }
495 };
496
497 for witness in witness_nodes {
498 let Some(secret) = witness_secrets.get(witness.id.as_str()) else {
500 return Err(DIDWebVHError::WitnessProofError(format!(
501 "Couldn't find secret for witness ({})",
502 witness.id
503 )));
504 };
505
506 validate_did_key_vm(secret.verification_method())?;
508
509 let proof = DataIntegrityProof::sign(
511 &json!({"versionId": log_entry.get_version_id()}),
512 secret,
513 SignOptions::new(),
514 )
515 .await
516 .map_err(|e| {
517 DIDWebVHError::SCIDError(format!(
518 "Couldn't generate Data Integrity Proof for LogEntry. Reason: {e}",
519 ))
520 })?;
521
522 witness_proofs
524 .add_proof(log_entry.get_version_id(), &proof, false)
525 .map_err(|e| DIDWebVHError::WitnessProofError(format!("Error adding proof: {e}")))?;
526 }
527
528 witness_proofs.write_optimise_records()?;
530
531 Ok(true)
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537 use crate::{DIDWebVHState, Multibase, witness::Witness};
538 use affinidi_secrets_resolver::secrets::Secret;
539 use serde_json::json;
540 use std::sync::Arc;
541
542 use crate::test_utils::{did_doc_with_key, key_and_params};
543
544 async fn create_log_entry_state(key: &Secret, params: &Parameters) -> (DIDWebVHState, String) {
546 let mut state = DIDWebVHState::default();
547 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", key);
548 state
549 .create_log_entry(None, &doc, params, key)
550 .await
551 .expect("Failed to create log entry");
552 let version_id = state
553 .log_entries
554 .last()
555 .unwrap()
556 .get_version_id()
557 .to_string();
558 (state, version_id)
559 }
560
561 #[test]
566 fn builder_missing_address() {
567 let (key, params) = key_and_params();
568 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
569 let result = CreateDIDConfig::builder()
570 .authorization_key(key)
571 .did_document(doc)
572 .parameters(params)
573 .build();
574
575 assert!(result.is_err());
576 }
577
578 #[test]
579 fn builder_missing_authorization_keys() {
580 let (key, params) = key_and_params();
581 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
582 let result = CreateDIDConfig::builder()
583 .address("https://example.com/")
584 .did_document(doc)
585 .parameters(params)
586 .build();
587
588 assert!(result.is_err());
589 }
590
591 #[test]
592 fn builder_missing_did_document() {
593 let (key, params) = key_and_params();
594 let result = CreateDIDConfig::builder()
595 .address("https://example.com/")
596 .authorization_key(key)
597 .parameters(params)
598 .build();
599
600 assert!(result.is_err());
601 }
602
603 #[test]
604 fn builder_missing_parameters() {
605 let (key, _) = key_and_params();
606 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
607 let result = CreateDIDConfig::builder()
608 .address("https://example.com/")
609 .authorization_key(key)
610 .did_document(doc)
611 .build();
612
613 assert!(result.is_err());
614 }
615
616 #[test]
617 fn builder_all_required_fields() {
618 let (key, params) = key_and_params();
619 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
620 let result = CreateDIDConfig::builder()
621 .address("https://example.com/")
622 .authorization_key(key)
623 .did_document(doc)
624 .parameters(params)
625 .build();
626
627 assert!(result.is_ok());
628 }
629
630 #[test]
631 fn builder_authorization_keys_replaces() {
632 let key1 = crate::test_utils::generate_signing_key();
633 let key2 = crate::test_utils::generate_signing_key();
634 let (_, params) = key_and_params();
635 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key2);
636
637 let config = CreateDIDConfig::builder()
638 .address("https://example.com/")
639 .authorization_key(key1)
640 .authorization_keys(vec![key2])
641 .did_document(doc)
642 .parameters(params)
643 .build()
644 .unwrap();
645
646 assert_eq!(config.authorization_keys.len(), 1);
647 }
648
649 #[test]
650 fn builder_witness_secrets() {
651 let (key, params) = key_and_params();
652 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
653 let witness_key = crate::test_utils::generate_signing_key();
654
655 let config = CreateDIDConfig::builder()
656 .address("https://example.com/")
657 .authorization_key(key)
658 .did_document(doc)
659 .parameters(params)
660 .witness_secret("did:key:z6Mk1", witness_key)
661 .build()
662 .unwrap();
663
664 assert_eq!(config.witness_secrets.len(), 1);
665 assert!(config.witness_secrets.contains_key("did:key:z6Mk1"));
666 }
667
668 #[test]
669 fn builder_defaults() {
670 let (key, params) = key_and_params();
671 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
672 let config = CreateDIDConfig::builder()
673 .address("https://example.com/")
674 .authorization_key(key)
675 .did_document(doc)
676 .parameters(params)
677 .build()
678 .unwrap();
679
680 assert!(!config.also_known_as_web);
681 assert!(!config.also_known_as_scid);
682 assert!(config.witness_secrets.is_empty());
683 }
684
685 #[tokio::test]
690 async fn create_did_with_url_address() {
691 let (key, params) = key_and_params();
692 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
693 let config = CreateDIDConfig::builder()
694 .address("https://example.com/")
695 .authorization_key(key)
696 .did_document(doc)
697 .parameters(params)
698 .build()
699 .unwrap();
700
701 let result = create_did(config).await;
702 assert!(result.is_ok());
703 let result = result.unwrap();
704 assert!(result.did.starts_with("did:webvh:"));
705 assert!(result.did.contains("example.com"));
706 assert!(!result.did.contains("{SCID}"));
707 }
708
709 #[tokio::test]
710 async fn create_did_with_did_address() {
711 let (key, params) = key_and_params();
712 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
713 let config = CreateDIDConfig::builder()
714 .address("did:webvh:{SCID}:example.com")
715 .authorization_key(key)
716 .did_document(doc)
717 .parameters(params)
718 .build()
719 .unwrap();
720
721 let result = create_did(config).await;
722 assert!(result.is_ok());
723 let result = result.unwrap();
724 assert!(result.did.starts_with("did:webvh:"));
725 assert!(!result.did.contains("{SCID}"));
726 }
727
728 #[tokio::test]
729 async fn create_did_invalid_address() {
730 let (key, _params) = key_and_params();
731 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
732 let config: CreateDIDConfig = CreateDIDConfig {
733 address: "not a valid url or did".to_string(),
734 authorization_keys: vec![key],
735 did_document: doc,
736 parameters: _params,
737 witness_secrets: HashMap::default(),
738 also_known_as_web: false,
739 also_known_as_scid: false,
740 version_time: None,
741 };
742
743 assert!(create_did(config).await.is_err());
744 }
745
746 #[tokio::test]
747 async fn create_did_no_update_keys() {
748 let key = crate::test_utils::generate_signing_key();
749 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
750 let params = Parameters::default(); let config = CreateDIDConfig::builder()
753 .address("https://example.com/")
754 .authorization_key(key)
755 .did_document(doc)
756 .parameters(params)
757 .build()
758 .unwrap();
759
760 assert!(create_did(config).await.is_err());
761 }
762
763 #[tokio::test]
764 async fn create_did_with_also_known_as_web() {
765 let (key, params) = key_and_params();
766 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
767 let config = CreateDIDConfig::builder()
768 .address("https://example.com/")
769 .authorization_key(key)
770 .did_document(doc)
771 .parameters(params)
772 .also_known_as_web(true)
773 .build()
774 .unwrap();
775
776 let result = create_did(config).await.unwrap();
777 let state = result.log_entry.get_state();
778 let also_known_as = state.get("alsoKnownAs").unwrap().as_array().unwrap();
779 assert!(
780 also_known_as
781 .iter()
782 .any(|v| { v.as_str().is_some_and(|s| s.starts_with("did:web:")) })
783 );
784 }
785
786 #[tokio::test]
787 async fn create_did_with_also_known_as_scid() {
788 let (key, params) = key_and_params();
789 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
790 let config = CreateDIDConfig::builder()
791 .address("https://example.com/")
792 .authorization_key(key)
793 .did_document(doc)
794 .parameters(params)
795 .also_known_as_scid(true)
796 .build()
797 .unwrap();
798
799 let result = create_did(config).await.unwrap();
800 let state = result.log_entry.get_state();
801 let also_known_as = state.get("alsoKnownAs").unwrap().as_array().unwrap();
802 assert!(
803 also_known_as
804 .iter()
805 .any(|v| { v.as_str().is_some_and(|s| s.starts_with("did:scid:vh:")) })
806 );
807 }
808
809 #[tokio::test]
810 async fn create_did_with_both_aliases() {
811 let (key, params) = key_and_params();
812 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
813 let config = CreateDIDConfig::builder()
814 .address("https://example.com/")
815 .authorization_key(key)
816 .did_document(doc)
817 .parameters(params)
818 .also_known_as_web(true)
819 .also_known_as_scid(true)
820 .build()
821 .unwrap();
822
823 let result = create_did(config).await.unwrap();
824 let state = result.log_entry.get_state();
825 let also_known_as = state.get("alsoKnownAs").unwrap().as_array().unwrap();
826 let has_web = also_known_as
827 .iter()
828 .any(|v| v.as_str().is_some_and(|s| s.starts_with("did:web:")));
829 let has_scid = also_known_as
830 .iter()
831 .any(|v| v.as_str().is_some_and(|s| s.starts_with("did:scid:vh:")));
832 assert!(has_web);
833 assert!(has_scid);
834 }
835
836 #[tokio::test]
837 async fn create_did_no_witnesses_returns_empty_proofs() {
838 let (key, params) = key_and_params();
839 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
840 let config = CreateDIDConfig::builder()
841 .address("https://example.com/")
842 .authorization_key(key)
843 .did_document(doc)
844 .parameters(params)
845 .build()
846 .unwrap();
847
848 let result = create_did(config).await.unwrap();
849 assert_eq!(result.witness_proofs.get_total_count(), 0);
850 }
851
852 #[tokio::test]
853 async fn create_did_with_witnesses() {
854 let (key, _) = key_and_params();
855 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
856 let witness1 = crate::test_utils::generate_signing_key();
857 let witness2 = crate::test_utils::generate_signing_key();
858 let w1_id = witness1.get_public_keymultibase().unwrap();
859 let w2_id = witness2.get_public_keymultibase().unwrap();
860
861 let params = Parameters {
862 update_keys: Some(Arc::new(vec![Multibase::new(
863 key.get_public_keymultibase().unwrap(),
864 )])),
865 witness: Some(Arc::new(Witnesses::Value {
866 threshold: 1,
867 witnesses: vec![
868 Witness {
869 id: Multibase::new(w1_id.clone()),
870 },
871 Witness {
872 id: Multibase::new(w2_id.clone()),
873 },
874 ],
875 })),
876 ..Default::default()
877 };
878
879 let config = CreateDIDConfig::builder()
880 .address("https://example.com/")
881 .authorization_key(key)
882 .did_document(doc)
883 .parameters(params)
884 .witness_secret(format!("did:key:{w1_id}"), witness1)
887 .witness_secret(format!("did:key:{w2_id}"), witness2)
888 .build()
889 .unwrap();
890
891 let result = create_did(config).await.unwrap();
892 assert_eq!(result.witness_proofs.get_total_count(), 2);
893 }
894
895 #[tokio::test]
896 async fn create_did_witnesses_missing_secret() {
897 let (key, _) = key_and_params();
898 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
899 let witness1 = crate::test_utils::generate_signing_key();
900 let w1_id = witness1.get_public_keymultibase().unwrap();
901
902 let params = Parameters {
903 update_keys: Some(Arc::new(vec![Multibase::new(
904 key.get_public_keymultibase().unwrap(),
905 )])),
906 witness: Some(Arc::new(Witnesses::Value {
907 threshold: 1,
908 witnesses: vec![Witness {
909 id: Multibase::new(w1_id),
910 }],
911 })),
912 ..Default::default()
913 };
914
915 let config = CreateDIDConfig::builder()
917 .address("https://example.com/")
918 .authorization_key(key)
919 .did_document(doc)
920 .parameters(params)
921 .build()
922 .unwrap();
923
924 assert!(create_did(config).await.is_err());
925 }
926
927 #[tokio::test]
928 async fn create_did_portable() {
929 let (key, _) = key_and_params();
930 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
931 let params = Parameters {
932 update_keys: Some(Arc::new(vec![Multibase::new(
933 key.get_public_keymultibase().unwrap(),
934 )])),
935 portable: Some(true),
936 ..Default::default()
937 };
938
939 let config = CreateDIDConfig::builder()
940 .address("https://example.com/")
941 .authorization_key(key)
942 .did_document(doc)
943 .parameters(params)
944 .build()
945 .unwrap();
946
947 let result = create_did(config).await.unwrap();
948 assert!(result.did.starts_with("did:webvh:"));
949 }
950
951 #[tokio::test]
952 async fn create_did_log_entry_serializable() {
953 let (key, params) = key_and_params();
954 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
955 let config = CreateDIDConfig::builder()
956 .address("https://example.com/")
957 .authorization_key(key)
958 .did_document(doc)
959 .parameters(params)
960 .build()
961 .unwrap();
962
963 let result = create_did(config).await.unwrap();
964 let json = serde_json::to_string(&result.log_entry);
965 assert!(json.is_ok());
966 assert!(!json.unwrap().is_empty());
967 }
968
969 #[test]
974 fn add_web_also_known_as_no_existing() {
975 let mut doc = json!({"id": "did:webvh:abc123:example.com"});
976 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
977
978 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
979 assert_eq!(aliases.len(), 1);
980 assert_eq!(aliases[0].as_str().unwrap(), "did:web:example.com");
981 }
982
983 #[test]
984 fn add_web_also_known_as_with_existing() {
985 let mut doc = json!({
986 "id": "did:webvh:abc123:example.com",
987 "alsoKnownAs": ["did:example:other"]
988 });
989 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
990
991 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
992 assert_eq!(aliases.len(), 2);
993 assert!(
994 aliases
995 .iter()
996 .any(|v| v.as_str() == Some("did:example:other"))
997 );
998 assert!(
999 aliases
1000 .iter()
1001 .any(|v| v.as_str() == Some("did:web:example.com"))
1002 );
1003 }
1004
1005 #[test]
1006 fn add_web_also_known_as_already_present() {
1007 let mut doc = json!({
1008 "id": "did:webvh:abc123:example.com",
1009 "alsoKnownAs": ["did:web:example.com"]
1010 });
1011 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1012
1013 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1014 assert_eq!(aliases.len(), 1);
1015 assert_eq!(aliases[0].as_str().unwrap(), "did:web:example.com");
1016 }
1017
1018 #[test]
1019 fn add_web_also_known_as_not_array() {
1020 let mut doc = json!({
1021 "id": "did:webvh:abc123:example.com",
1022 "alsoKnownAs": "not an array"
1023 });
1024 assert!(add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").is_err());
1025 }
1026
1027 #[test]
1032 fn add_scid_also_known_as_no_existing() {
1033 let mut doc = json!({"id": "did:webvh:abc123:example.com"});
1034 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1035
1036 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1037 assert_eq!(aliases.len(), 1);
1038 assert!(aliases[0].as_str().unwrap().starts_with("did:scid:vh:1:"));
1039 }
1040
1041 #[test]
1042 fn add_scid_also_known_as_with_existing() {
1043 let mut doc = json!({
1044 "id": "did:webvh:abc123:example.com",
1045 "alsoKnownAs": ["did:example:other"]
1046 });
1047 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1048
1049 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1050 assert_eq!(aliases.len(), 2);
1051 assert!(
1052 aliases
1053 .iter()
1054 .any(|v| v.as_str() == Some("did:example:other"))
1055 );
1056 assert!(
1057 aliases
1058 .iter()
1059 .any(|v| { v.as_str().is_some_and(|s| s.starts_with("did:scid:vh:1:")) })
1060 );
1061 }
1062
1063 #[test]
1064 fn add_scid_also_known_as_already_present() {
1065 let scid_id = DIDWebVHState::convert_webvh_id_to_scid_id("did:webvh:abc123:example.com");
1066 let mut doc = json!({
1067 "id": "did:webvh:abc123:example.com",
1068 "alsoKnownAs": [scid_id]
1069 });
1070 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1071
1072 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1073 assert_eq!(aliases.len(), 1);
1074 }
1075
1076 #[test]
1077 fn add_scid_also_known_as_not_array() {
1078 let mut doc = json!({
1079 "id": "did:webvh:abc123:example.com",
1080 "alsoKnownAs": 42
1081 });
1082 assert!(add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").is_err());
1083 }
1084
1085 #[tokio::test]
1090 async fn sign_witness_proofs_no_witnesses() {
1091 let (key, params) = key_and_params();
1092 let (state, _) = create_log_entry_state(&key, ¶ms).await;
1093 let log_entry = state.log_entries.last().unwrap();
1094
1095 let mut proofs = WitnessProofCollection::default();
1096 let result = sign_witness_proofs(
1097 &mut proofs,
1098 log_entry,
1099 &None,
1100 &HashMap::<String, Secret>::default(),
1101 )
1102 .await;
1103 assert!(result.is_ok());
1104 assert!(!result.unwrap()); assert_eq!(proofs.get_total_count(), 0);
1106 }
1107
1108 #[tokio::test]
1109 async fn sign_witness_proofs_with_witnesses() {
1110 let (key, _) = key_and_params();
1111 let witness1 = crate::test_utils::generate_signing_key();
1112 let witness2 = crate::test_utils::generate_signing_key();
1113 let w1_id = witness1.get_public_keymultibase().unwrap();
1114 let w2_id = witness2.get_public_keymultibase().unwrap();
1115
1116 let params = Parameters {
1117 update_keys: Some(Arc::new(vec![Multibase::new(
1118 key.get_public_keymultibase().unwrap(),
1119 )])),
1120 witness: Some(Arc::new(Witnesses::Value {
1121 threshold: 1,
1122 witnesses: vec![
1123 Witness {
1124 id: Multibase::new(w1_id.clone()),
1125 },
1126 Witness {
1127 id: Multibase::new(w2_id.clone()),
1128 },
1129 ],
1130 })),
1131 ..Default::default()
1132 };
1133
1134 let (state, version_id) = create_log_entry_state(&key, ¶ms).await;
1135 let log_entry = state.log_entries.last().unwrap();
1136
1137 let mut secrets = HashMap::default();
1138 secrets.insert(format!("did:key:{w1_id}"), witness1);
1140 secrets.insert(format!("did:key:{w2_id}"), witness2);
1141
1142 let witnesses = log_entry.get_active_witnesses();
1143 let mut proofs = WitnessProofCollection::default();
1144 let result = sign_witness_proofs(&mut proofs, log_entry, &witnesses, &secrets).await;
1145 assert!(result.is_ok());
1146 assert!(result.unwrap()); assert_eq!(proofs.get_proof_count(&version_id), 2);
1148 }
1149
1150 #[tokio::test]
1151 async fn sign_witness_proofs_missing_secret() {
1152 let (key, _) = key_and_params();
1153 let witness1 = crate::test_utils::generate_signing_key();
1154 let w1_id = witness1.get_public_keymultibase().unwrap();
1155
1156 let params = Parameters {
1157 update_keys: Some(Arc::new(vec![Multibase::new(
1158 key.get_public_keymultibase().unwrap(),
1159 )])),
1160 witness: Some(Arc::new(Witnesses::Value {
1161 threshold: 1,
1162 witnesses: vec![Witness {
1163 id: Multibase::new(w1_id),
1164 }],
1165 })),
1166 ..Default::default()
1167 };
1168
1169 let (state, _) = create_log_entry_state(&key, ¶ms).await;
1170 let log_entry = state.log_entries.last().unwrap();
1171
1172 let witnesses = log_entry.get_active_witnesses();
1173 let mut proofs = WitnessProofCollection::default();
1174 let result = sign_witness_proofs(
1176 &mut proofs,
1177 log_entry,
1178 &witnesses,
1179 &HashMap::<String, Secret>::default(),
1180 )
1181 .await;
1182 assert!(result.is_err());
1183 }
1184
1185 #[tokio::test]
1186 async fn sign_witness_proofs_empty_witnesses_config() {
1187 let (key, params) = key_and_params();
1188 let (state, _) = create_log_entry_state(&key, ¶ms).await;
1189 let log_entry = state.log_entries.last().unwrap();
1190
1191 let witnesses = Some(Arc::new(Witnesses::Empty {}));
1192 let mut proofs = WitnessProofCollection::default();
1193 let result = sign_witness_proofs(
1194 &mut proofs,
1195 log_entry,
1196 &witnesses,
1197 &HashMap::<String, Secret>::default(),
1198 )
1199 .await;
1200 assert!(result.is_err());
1201 }
1202
1203 #[test]
1208 fn validate_did_key_vm_accepts_valid() {
1209 let vm = "did:key:z6MkTest#z6MkTest";
1210 assert!(validate_did_key_vm(vm).is_ok());
1211 }
1212
1213 #[test]
1214 fn validate_did_key_vm_rejects_missing_hash() {
1215 let vm = "did:key:z6MkTest";
1216 assert!(validate_did_key_vm(vm).is_err());
1217 }
1218
1219 #[test]
1220 fn validate_did_key_vm_rejects_wrong_prefix() {
1221 let vm = "did:web:example.com#key-0";
1222 assert!(validate_did_key_vm(vm).is_err());
1223 }
1224
1225 #[test]
1226 fn validate_did_key_vm_rejects_empty() {
1227 assert!(validate_did_key_vm("").is_err());
1228 }
1229
1230 #[test]
1235 fn builder_also_known_as_flags() {
1236 let (key, params) = key_and_params();
1237 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1238 let config = CreateDIDConfig::builder()
1239 .address("https://example.com/")
1240 .authorization_key(key)
1241 .did_document(doc)
1242 .parameters(params)
1243 .also_known_as_web(true)
1244 .also_known_as_scid(true)
1245 .build()
1246 .unwrap();
1247
1248 assert!(config.also_known_as_web);
1249 assert!(config.also_known_as_scid);
1250 }
1251
1252 #[test]
1253 fn builder_multiple_authorization_keys_accumulate() {
1254 let key1 = crate::test_utils::generate_signing_key();
1255 let key2 = crate::test_utils::generate_signing_key();
1256 let (_, params) = key_and_params();
1257 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key1);
1258
1259 let config = CreateDIDConfig::builder()
1260 .address("https://example.com/")
1261 .authorization_key(key1)
1262 .authorization_key(key2)
1263 .did_document(doc)
1264 .parameters(params)
1265 .build()
1266 .unwrap();
1267
1268 assert_eq!(config.authorization_keys.len(), 2);
1269 }
1270
1271 #[test]
1272 fn builder_witness_secrets_bulk_replaces() {
1273 let (key, params) = key_and_params();
1274 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1275 let w1 = crate::test_utils::generate_signing_key();
1276 let w2 = crate::test_utils::generate_signing_key();
1277
1278 let mut bulk = HashMap::default();
1279 bulk.insert("did:key:z6MkBulk".to_string(), w2);
1280
1281 let config = CreateDIDConfig::builder()
1282 .address("https://example.com/")
1283 .authorization_key(key)
1284 .did_document(doc)
1285 .parameters(params)
1286 .witness_secret("did:key:z6MkSingle", w1)
1287 .witness_secrets(bulk)
1288 .build()
1289 .unwrap();
1290
1291 assert_eq!(config.witness_secrets.len(), 1);
1293 assert!(config.witness_secrets.contains_key("did:key:z6MkBulk"));
1294 assert!(!config.witness_secrets.contains_key("did:key:z6MkSingle"));
1295 }
1296
1297 #[tokio::test]
1302 async fn create_did_key_with_existing_did_key_id() {
1303 let mut key = crate::test_utils::generate_signing_key();
1304 let pub_mb = key.get_public_keymultibase().unwrap();
1305 key.id = format!("did:key:{pub_mb}#{pub_mb}");
1306
1307 let params = Parameters {
1308 update_keys: Some(Arc::new(vec![Multibase::new(pub_mb)])),
1309 ..Default::default()
1310 };
1311 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1312
1313 let config = CreateDIDConfig::builder()
1314 .address("https://example.com/")
1315 .authorization_key(key)
1316 .did_document(doc)
1317 .parameters(params)
1318 .build()
1319 .unwrap();
1320
1321 let result = create_did(config).await;
1322 assert!(result.is_ok());
1323 }
1324
1325 #[tokio::test]
1326 async fn create_did_state_has_no_scid_placeholder() {
1327 let (key, params) = key_and_params();
1328 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1329 let config = CreateDIDConfig::builder()
1330 .address("https://example.com/")
1331 .authorization_key(key)
1332 .did_document(doc)
1333 .parameters(params)
1334 .build()
1335 .unwrap();
1336
1337 let result = create_did(config).await.unwrap();
1338
1339 let state_str = serde_json::to_string(result.log_entry.get_state()).unwrap();
1341 assert!(!state_str.contains("{SCID}"));
1342 assert!(!result.did.contains("{SCID}"));
1343 }
1344
1345 #[tokio::test]
1346 async fn create_did_log_entry_has_proof() {
1347 let (key, params) = key_and_params();
1348 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1349 let config = CreateDIDConfig::builder()
1350 .address("https://example.com/")
1351 .authorization_key(key)
1352 .did_document(doc)
1353 .parameters(params)
1354 .build()
1355 .unwrap();
1356
1357 let result = create_did(config).await.unwrap();
1358 assert!(!result.log_entry.get_proofs().is_empty());
1359 }
1360
1361 #[tokio::test]
1362 async fn create_did_version_id_starts_with_one() {
1363 let (key, params) = key_and_params();
1364 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1365 let config = CreateDIDConfig::builder()
1366 .address("https://example.com/")
1367 .authorization_key(key)
1368 .did_document(doc)
1369 .parameters(params)
1370 .build()
1371 .unwrap();
1372
1373 let result = create_did(config).await.unwrap();
1374 let version_id = result.log_entry.get_version_id();
1375 assert!(version_id.starts_with("1-"));
1376 }
1377
1378 #[tokio::test]
1379 async fn create_did_with_url_path() {
1380 let (key, params) = key_and_params();
1381 let doc = did_doc_with_key("did:webvh:{SCID}:example.com:dids:alice", &key);
1382 let config = CreateDIDConfig::builder()
1383 .address("https://example.com/dids/alice/")
1384 .authorization_key(key)
1385 .did_document(doc)
1386 .parameters(params)
1387 .build()
1388 .unwrap();
1389
1390 let result = create_did(config).await.unwrap();
1391 assert!(result.did.starts_with("did:webvh:"));
1392 assert!(result.did.contains("example.com"));
1393 }
1394
1395 #[tokio::test]
1396 async fn create_did_result_did_matches_state_id() {
1397 let (key, params) = key_and_params();
1398 let doc = did_doc_with_key("did:webvh:{SCID}:example.com", &key);
1399 let config = CreateDIDConfig::builder()
1400 .address("https://example.com/")
1401 .authorization_key(key)
1402 .did_document(doc)
1403 .parameters(params)
1404 .build()
1405 .unwrap();
1406
1407 let result = create_did(config).await.unwrap();
1408 let state_id = result
1409 .log_entry
1410 .get_state()
1411 .get("id")
1412 .unwrap()
1413 .as_str()
1414 .unwrap();
1415 assert_eq!(result.did, state_id);
1416 }
1417
1418 #[test]
1423 fn add_web_also_known_as_empty_array() {
1424 let mut doc = json!({
1425 "id": "did:webvh:abc123:example.com",
1426 "alsoKnownAs": []
1427 });
1428 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1429
1430 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1431 assert_eq!(aliases.len(), 1);
1432 assert_eq!(aliases[0].as_str().unwrap(), "did:web:example.com");
1433 }
1434
1435 #[test]
1436 fn add_web_also_known_as_idempotent() {
1437 let mut doc = json!({"id": "did:webvh:abc123:example.com"});
1438 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1439 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1440
1441 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1442 assert_eq!(aliases.len(), 1);
1443 assert_eq!(aliases[0].as_str().unwrap(), "did:web:example.com");
1444 }
1445
1446 #[test]
1447 fn add_web_also_known_as_preserves_all_existing() {
1448 let mut doc = json!({
1449 "id": "did:webvh:abc123:example.com",
1450 "alsoKnownAs": ["did:example:a", "did:example:b", "did:web:example.com"]
1451 });
1452 add_web_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1453
1454 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1455 assert_eq!(aliases.len(), 3);
1456 assert!(aliases.iter().any(|v| v.as_str() == Some("did:example:a")));
1457 assert!(aliases.iter().any(|v| v.as_str() == Some("did:example:b")));
1458 assert!(
1459 aliases
1460 .iter()
1461 .any(|v| v.as_str() == Some("did:web:example.com"))
1462 );
1463 }
1464
1465 #[test]
1470 fn add_scid_also_known_as_empty_array() {
1471 let mut doc = json!({
1472 "id": "did:webvh:abc123:example.com",
1473 "alsoKnownAs": []
1474 });
1475 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1476
1477 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1478 assert_eq!(aliases.len(), 1);
1479 assert!(aliases[0].as_str().unwrap().starts_with("did:scid:vh:1:"));
1480 }
1481
1482 #[test]
1483 fn add_scid_also_known_as_idempotent() {
1484 let mut doc = json!({"id": "did:webvh:abc123:example.com"});
1485 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1486 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1487
1488 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1489 assert_eq!(aliases.len(), 1);
1490 }
1491
1492 #[test]
1493 fn add_scid_also_known_as_preserves_all_existing() {
1494 let scid_id = DIDWebVHState::convert_webvh_id_to_scid_id("did:webvh:abc123:example.com");
1495 let mut doc = json!({
1496 "id": "did:webvh:abc123:example.com",
1497 "alsoKnownAs": ["did:example:a", "did:example:b", scid_id]
1498 });
1499 add_scid_also_known_as(&mut doc, "did:webvh:abc123:example.com").unwrap();
1500
1501 let aliases = doc.get("alsoKnownAs").unwrap().as_array().unwrap();
1502 assert_eq!(aliases.len(), 3);
1503 assert!(aliases.iter().any(|v| v.as_str() == Some("did:example:a")));
1504 assert!(aliases.iter().any(|v| v.as_str() == Some("did:example:b")));
1505 }
1506
1507 #[tokio::test]
1512 async fn sign_witness_proofs_are_verifiable() {
1513 let (key, _) = key_and_params();
1514 let witness1 = crate::test_utils::generate_signing_key();
1515 let w1_id = witness1.get_public_keymultibase().unwrap();
1516
1517 let params = Parameters {
1518 update_keys: Some(Arc::new(vec![Multibase::new(
1519 key.get_public_keymultibase().unwrap(),
1520 )])),
1521 witness: Some(Arc::new(Witnesses::Value {
1522 threshold: 1,
1523 witnesses: vec![Witness {
1524 id: Multibase::new(w1_id.clone()),
1525 }],
1526 })),
1527 ..Default::default()
1528 };
1529
1530 let (state, version_id) = create_log_entry_state(&key, ¶ms).await;
1531 let log_entry_state = state.log_entries.last().unwrap();
1532
1533 let mut secrets = HashMap::default();
1534 secrets.insert(format!("did:key:{w1_id}"), witness1);
1536
1537 let witnesses = log_entry_state.get_active_witnesses();
1538 let mut proofs = WitnessProofCollection::default();
1539 sign_witness_proofs(&mut proofs, log_entry_state, &witnesses, &secrets)
1540 .await
1541 .unwrap();
1542
1543 let witness_proof = proofs.get_proofs(&version_id).unwrap();
1545 let validation = log_entry_state.log_entry.validate_witness_proof(
1546 witness_proof.proof.first().unwrap(),
1547 &crate::witness::WitnessVerifyOptions::new(),
1548 );
1549 assert!(validation.is_ok());
1550 }
1551
1552 #[tokio::test]
1553 async fn sign_witness_proofs_returns_true_with_witnesses() {
1554 let (key, _) = key_and_params();
1555 let witness1 = crate::test_utils::generate_signing_key();
1556 let w1_id = witness1.get_public_keymultibase().unwrap();
1557
1558 let params = Parameters {
1559 update_keys: Some(Arc::new(vec![Multibase::new(
1560 key.get_public_keymultibase().unwrap(),
1561 )])),
1562 witness: Some(Arc::new(Witnesses::Value {
1563 threshold: 1,
1564 witnesses: vec![Witness {
1565 id: Multibase::new(w1_id.clone()),
1566 }],
1567 })),
1568 ..Default::default()
1569 };
1570
1571 let (state, _) = create_log_entry_state(&key, ¶ms).await;
1572 let log_entry_state = state.log_entries.last().unwrap();
1573
1574 let mut secrets = HashMap::default();
1575 secrets.insert(format!("did:key:{w1_id}"), witness1);
1577
1578 let witnesses = log_entry_state.get_active_witnesses();
1579 let mut proofs = WitnessProofCollection::default();
1580 let signed = sign_witness_proofs(&mut proofs, log_entry_state, &witnesses, &secrets)
1581 .await
1582 .unwrap();
1583 assert!(signed);
1584 }
1585
1586 #[tokio::test]
1587 async fn sign_witness_proofs_returns_false_no_witnesses() {
1588 let (key, params) = key_and_params();
1589 let (state, _) = create_log_entry_state(&key, ¶ms).await;
1590 let log_entry = state.log_entries.last().unwrap();
1591
1592 let mut proofs = WitnessProofCollection::default();
1593 let signed = sign_witness_proofs(
1594 &mut proofs,
1595 log_entry,
1596 &None,
1597 &HashMap::<String, Secret>::default(),
1598 )
1599 .await
1600 .unwrap();
1601 assert!(!signed);
1602 }
1603
1604 #[test]
1605 fn replace_did_placeholder_replaces_all_occurrences() {
1606 let did = "did:webvh:abc:example.com".to_string();
1607
1608 let mut did_document = json!({
1609 "id": "{DID}",
1610 "@context": ["https://www.w3.org/ns/did/v1"],
1611 "verificationMethod": [{
1612 "id": "{DID}#key-0",
1613 "type": "Multikey",
1614 "publicKeyMultibase": "abcd",
1615 "controller": "{DID}"
1616 }],
1617 "authentication": ["{DID}#key-0"],
1618 "assertionMethod": ["{DID}#key-0"],
1619 });
1620
1621 let expected_document = json!({
1622 "id": "did:webvh:abc:example.com",
1623 "@context": ["https://www.w3.org/ns/did/v1"],
1624 "verificationMethod": [{
1625 "id": "did:webvh:abc:example.com#key-0",
1626 "type": "Multikey",
1627 "publicKeyMultibase": "abcd",
1628 "controller": did
1629 }],
1630 "authentication": ["did:webvh:abc:example.com#key-0"],
1631 "assertionMethod": ["did:webvh:abc:example.com#key-0"],
1632 });
1633
1634 replace_did_placeholder(&mut did_document, &did);
1635
1636 assert_eq!(did_document, expected_document);
1637 }
1638
1639 #[test]
1640 fn replace_did_placeholder_no_op() {
1641 let did = "did:webvh:abc:example.com".to_string();
1642
1643 let mut did_document = json!({
1644 "a": 1,
1645 "b": {
1646 "c": null
1647 }
1648 });
1649
1650 let expected_document = json!({
1651 "a": 1,
1652 "b": {
1653 "c": null
1654 }
1655 });
1656
1657 replace_did_placeholder(&mut did_document, &did);
1658
1659 assert_eq!(did_document, expected_document);
1660 }
1661}