1use crate::wallet::error::WalletError;
8use crate::wallet::interfaces::*;
9
10fn invalid(field: &str, requirement: &str) -> WalletError {
15 WalletError::InvalidParameter(format!("{field}: must be {requirement}"))
16}
17
18fn validate_string_length(s: &str, name: &str, min: usize, max: usize) -> Result<(), WalletError> {
19 let len = s.len();
20 if len < min {
21 return Err(invalid(name, &format!("at least {min} bytes")));
22 }
23 if len > max {
24 return Err(invalid(name, &format!("no more than {max} bytes")));
25 }
26 Ok(())
27}
28
29fn validate_label(s: &str) -> Result<(), WalletError> {
30 validate_string_length(s, "label", 1, 300)
31}
32
33fn validate_tag(s: &str) -> Result<(), WalletError> {
34 validate_string_length(s, "tag", 1, 300)
35}
36
37fn validate_basket(s: &str) -> Result<(), WalletError> {
38 validate_basket_name(s)
39}
40
41fn validate_description(s: &str, name: &str) -> Result<(), WalletError> {
42 validate_string_length(s, name, 5, 2000)
43}
44
45fn validate_optional_limit(limit: Option<u32>) -> Result<(), WalletError> {
46 if let Some(v) = limit {
47 if !(1..=10000).contains(&v) {
48 return Err(invalid("limit", "between 1 and 10000"));
49 }
50 }
51 Ok(())
52}
53
54pub fn normalize_identifier(s: &str) -> String {
57 s.trim().to_lowercase()
58}
59
60fn validate_protocol_id(protocol: &crate::wallet::types::Protocol) -> Result<(), WalletError> {
61 if protocol.security_level > 2 {
62 return Err(invalid("protocol_id.security_level", "0, 1, or 2"));
63 }
64 let normalized = normalize_identifier(&protocol.protocol);
65 if normalized.is_empty() {
66 return Err(invalid("protocol_id.protocol", "non-empty"));
67 }
68 validate_string_length(&normalized, "protocol_id.protocol", 1, 400)?;
69 if normalized.contains(" ") {
71 return Err(invalid(
72 "protocol_id.protocol",
73 "free of consecutive spaces",
74 ));
75 }
76 if !normalized
78 .chars()
79 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == ' ')
80 {
81 return Err(invalid(
82 "protocol_id.protocol",
83 "only lowercase letters, numbers, and spaces",
84 ));
85 }
86 if normalized.ends_with("protocol") {
88 return Err(invalid(
89 "protocol_id.protocol",
90 "not ending with 'protocol'",
91 ));
92 }
93 if normalized == "p" || normalized.starts_with("p ") {
105 return Err(invalid(
106 "protocol_id.protocol",
107 "not in the reserved 'p' namespace (a name of the form `p <...>`)",
108 ));
109 }
110 Ok(())
111}
112
113fn validate_basket_name(s: &str) -> Result<(), WalletError> {
114 let normalized = normalize_identifier(s);
115 validate_string_length(&normalized, "basket", 5, 300)?;
116 if !normalized
118 .chars()
119 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == ' ')
120 {
121 return Err(invalid(
122 "basket",
123 "only lowercase letters, numbers, and spaces",
124 ));
125 }
126 if normalized.contains(" ") {
128 return Err(invalid("basket", "free of consecutive spaces"));
129 }
130 if normalized.ends_with("basket") {
132 return Err(invalid("basket", "not ending with 'basket'"));
133 }
134 if normalized.starts_with("admin") {
136 return Err(invalid("basket", "not starting with 'admin'"));
137 }
138 if normalized == "default" {
140 return Err(invalid("basket", "not 'default'"));
141 }
142 if normalized == "p" || normalized.starts_with("p ") {
147 return Err(invalid(
148 "basket",
149 "not in the reserved 'p' namespace (a name of the form `p <...>`)",
150 ));
151 }
152 Ok(())
153}
154
155fn validate_key_id(key_id: &str) -> Result<(), WalletError> {
156 if key_id.is_empty() {
157 return Err(invalid("key_id", "non-empty"));
158 }
159 validate_string_length(key_id, "key_id", 1, 800)
160}
161
162fn validate_privileged_reason(
163 privileged: bool,
164 reason: &Option<String>,
165) -> Result<(), WalletError> {
166 if privileged {
167 if let Some(r) = reason {
168 validate_string_length(r, "privileged_reason", 5, 50)?;
169 } else {
170 return Err(invalid(
171 "privileged_reason",
172 "provided when privileged is true",
173 ));
174 }
175 }
176 Ok(())
177}
178
179fn validate_optional_privileged_reason(
180 privileged: Option<bool>,
181 reason: &Option<String>,
182) -> Result<(), WalletError> {
183 if privileged.unwrap_or(false) {
184 if let Some(r) = reason {
185 validate_string_length(r, "privileged_reason", 5, 50)?;
186 } else {
187 return Err(invalid(
188 "privileged_reason",
189 "provided when privileged is true",
190 ));
191 }
192 }
193 Ok(())
194}
195
196pub fn validate_create_action_args(args: &CreateActionArgs) -> Result<(), WalletError> {
202 validate_description(&args.description, "description")?;
203
204 for label in &args.labels {
205 validate_label(label)?;
206 }
207
208 for output in &args.outputs {
209 if output.locking_script.is_none() && output.output_description.is_empty() {
210 return Err(invalid(
211 "output",
212 "has locking_script or output_description",
213 ));
214 }
215 for tag in &output.tags {
216 validate_tag(tag)?;
217 }
218 if let Some(ref basket) = output.basket {
219 validate_basket(basket)?;
220 }
221 }
222
223 for input in &args.inputs {
224 if input.unlocking_script.is_none() && input.unlocking_script_length.is_none() {
225 return Err(invalid(
226 "input",
227 "has unlocking_script or unlocking_script_length",
228 ));
229 }
230 }
231
232 Ok(())
233}
234
235pub fn validate_sign_action_args(args: &SignActionArgs) -> Result<(), WalletError> {
237 if args.reference.is_empty() {
238 return Err(invalid("reference", "non-empty"));
239 }
240 if args.spends.is_empty() {
241 return Err(invalid("spends", "at least one spend"));
242 }
243 for spend in args.spends.values() {
244 if spend.unlocking_script.is_empty() {
245 return Err(invalid("unlocking_script", "non-empty"));
246 }
247 }
248 Ok(())
249}
250
251pub fn validate_abort_action_args(args: &AbortActionArgs) -> Result<(), WalletError> {
253 if args.reference.is_empty() {
254 return Err(invalid("reference", "non-empty"));
255 }
256 Ok(())
257}
258
259pub fn validate_list_actions_args(args: &ListActionsArgs) -> Result<(), WalletError> {
261 if args.labels.is_empty() {
262 return Err(invalid("labels", "non-empty"));
263 }
264 for label in &args.labels {
265 validate_label(label)?;
266 }
267 validate_optional_limit(args.limit)?;
268 Ok(())
269}
270
271pub fn validate_internalize_action_args(args: &InternalizeActionArgs) -> Result<(), WalletError> {
273 if args.tx.is_empty() {
274 return Err(invalid("tx", "non-empty"));
275 }
276 if args.outputs.is_empty() {
277 return Err(invalid("outputs", "at least one output"));
278 }
279 validate_description(&args.description, "description")?;
280 for label in &args.labels {
281 validate_label(label)?;
282 }
283 let _ = &args.outputs;
286 Ok(())
287}
288
289pub fn validate_list_outputs_args(args: &ListOutputsArgs) -> Result<(), WalletError> {
291 validate_basket(&args.basket)?;
292 validate_optional_limit(args.limit)?;
293 for tag in &args.tags {
294 validate_tag(tag)?;
295 }
296 Ok(())
297}
298
299pub fn validate_relinquish_output_args(args: &RelinquishOutputArgs) -> Result<(), WalletError> {
301 validate_basket(&args.basket)?;
302 if args.output.is_empty() {
303 return Err(invalid("output", "non-empty"));
304 }
305 Ok(())
306}
307
308pub fn validate_get_public_key_args(args: &GetPublicKeyArgs) -> Result<(), WalletError> {
310 if !args.identity_key {
311 if args.protocol_id.is_none() {
312 return Err(invalid(
313 "protocol_id",
314 "provided when identity_key is false",
315 ));
316 }
317 if args.key_id.is_none() {
318 return Err(invalid("key_id", "provided when identity_key is false"));
319 }
320 if let Some(ref p) = args.protocol_id {
321 validate_protocol_id(p)?;
322 }
323 if let Some(ref k) = args.key_id {
324 validate_key_id(k)?;
325 }
326 }
327 Ok(())
328}
329
330pub fn validate_encrypt_args(args: &EncryptArgs) -> Result<(), WalletError> {
332 validate_protocol_id(&args.protocol_id)?;
333 validate_key_id(&args.key_id)?;
334 if args.plaintext.is_empty() {
335 return Err(invalid("plaintext", "non-empty"));
336 }
337 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
338 Ok(())
339}
340
341pub fn validate_decrypt_args(args: &DecryptArgs) -> Result<(), WalletError> {
343 validate_protocol_id(&args.protocol_id)?;
344 validate_key_id(&args.key_id)?;
345 if args.ciphertext.is_empty() {
346 return Err(invalid("ciphertext", "non-empty"));
347 }
348 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
349 Ok(())
350}
351
352pub fn validate_create_hmac_args(args: &CreateHmacArgs) -> Result<(), WalletError> {
354 validate_protocol_id(&args.protocol_id)?;
355 validate_key_id(&args.key_id)?;
356 if args.data.is_empty() {
357 return Err(invalid("data", "non-empty"));
358 }
359 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
360 Ok(())
361}
362
363pub fn validate_verify_hmac_args(args: &VerifyHmacArgs) -> Result<(), WalletError> {
365 validate_protocol_id(&args.protocol_id)?;
366 validate_key_id(&args.key_id)?;
367 if args.data.is_empty() {
368 return Err(invalid("data", "non-empty"));
369 }
370 if args.hmac.is_empty() {
371 return Err(invalid("hmac", "non-empty"));
372 }
373 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
374 Ok(())
375}
376
377pub fn validate_create_signature_args(args: &CreateSignatureArgs) -> Result<(), WalletError> {
379 validate_protocol_id(&args.protocol_id)?;
380 validate_key_id(&args.key_id)?;
381 let has_data = args.data.as_ref().is_some_and(|d| !d.is_empty());
382 let has_hash = args
383 .hash_to_directly_sign
384 .as_ref()
385 .is_some_and(|h| !h.is_empty());
386 if !has_data && !has_hash {
387 return Err(invalid(
388 "data",
389 "provided (either data or hash_to_directly_sign)",
390 ));
391 }
392 if has_hash {
393 if let Some(ref h) = args.hash_to_directly_sign {
394 if h.len() != 32 {
395 return Err(invalid("hash_to_directly_sign", "exactly 32 bytes"));
396 }
397 }
398 }
399 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
400 Ok(())
401}
402
403pub fn validate_verify_signature_args(args: &VerifySignatureArgs) -> Result<(), WalletError> {
405 validate_protocol_id(&args.protocol_id)?;
406 validate_key_id(&args.key_id)?;
407 let has_data = args.data.as_ref().is_some_and(|d| !d.is_empty());
408 let has_hash = args
409 .hash_to_directly_verify
410 .as_ref()
411 .is_some_and(|h| !h.is_empty());
412 if !has_data && !has_hash {
413 return Err(invalid(
414 "data",
415 "provided (either data or hash_to_directly_verify)",
416 ));
417 }
418 if has_hash {
419 if let Some(ref h) = args.hash_to_directly_verify {
420 if h.len() != 32 {
421 return Err(invalid("hash_to_directly_verify", "exactly 32 bytes"));
422 }
423 }
424 }
425 if args.signature.is_empty() {
426 return Err(invalid("signature", "non-empty"));
427 }
428 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
429 Ok(())
430}
431
432pub fn validate_acquire_certificate_args(args: &AcquireCertificateArgs) -> Result<(), WalletError> {
434 match args.acquisition_protocol {
435 AcquisitionProtocol::Direct => {
436 if args.serial_number.is_none() {
437 return Err(invalid("serial_number", "provided for direct acquisition"));
438 }
439 if args.signature.is_none() {
440 return Err(invalid("signature", "provided for direct acquisition"));
441 }
442 if args.revocation_outpoint.is_none() {
443 return Err(invalid(
444 "revocation_outpoint",
445 "provided for direct acquisition",
446 ));
447 }
448 if args.keyring_revealer.is_none() {
449 return Err(invalid(
450 "keyring_revealer",
451 "provided for direct acquisition",
452 ));
453 }
454 if args.keyring_for_subject.is_none() {
455 return Err(invalid(
456 "keyring_for_subject",
457 "provided for direct acquisition",
458 ));
459 }
460 }
461 AcquisitionProtocol::Issuance => {
462 if args.certifier_url.is_none() {
463 return Err(invalid(
464 "certifier_url",
465 "provided for issuance acquisition",
466 ));
467 }
468 }
469 }
470 validate_privileged_reason(args.privileged, &args.privileged_reason)?;
471 for field_name in args.fields.keys() {
473 validate_string_length(field_name, "field_name", 1, 50)?;
474 }
475 Ok(())
476}
477
478pub fn validate_list_certificates_args(args: &ListCertificatesArgs) -> Result<(), WalletError> {
480 validate_optional_limit(args.limit)?;
481 validate_optional_privileged_reason(args.privileged.0, &args.privileged_reason)?;
482 Ok(())
483}
484
485pub fn validate_prove_certificate_args(args: &ProveCertificateArgs) -> Result<(), WalletError> {
487 if args.fields_to_reveal.is_empty() {
488 return Err(invalid("fields_to_reveal", "non-empty"));
489 }
490 for field in &args.fields_to_reveal {
491 validate_string_length(field, "fields_to_reveal entry", 1, 50)?;
492 }
493 validate_optional_privileged_reason(args.privileged.0, &args.privileged_reason)?;
494 Ok(())
495}
496
497pub fn validate_relinquish_certificate_args(
499 args: &RelinquishCertificateArgs,
500) -> Result<(), WalletError> {
501 let _ = args;
503 Ok(())
504}
505
506pub fn validate_discover_by_identity_key_args(
508 args: &DiscoverByIdentityKeyArgs,
509) -> Result<(), WalletError> {
510 let _ = args;
512 Ok(())
513}
514
515pub fn validate_discover_by_attributes_args(
517 args: &DiscoverByAttributesArgs,
518) -> Result<(), WalletError> {
519 if args.attributes.is_empty() {
520 return Err(invalid("attributes", "non-empty"));
521 }
522 Ok(())
523}
524
525pub fn validate_reveal_counterparty_key_linkage_args(
527 args: &RevealCounterpartyKeyLinkageArgs,
528) -> Result<(), WalletError> {
529 validate_optional_privileged_reason(args.privileged, &args.privileged_reason)?;
530 Ok(())
531}
532
533pub fn validate_reveal_specific_key_linkage_args(
535 args: &RevealSpecificKeyLinkageArgs,
536) -> Result<(), WalletError> {
537 validate_protocol_id(&args.protocol_id)?;
538 validate_key_id(&args.key_id)?;
539 validate_optional_privileged_reason(args.privileged, &args.privileged_reason)?;
540 Ok(())
541}
542
543pub fn validate_get_header_args(args: &GetHeaderArgs) -> Result<(), WalletError> {
545 if args.height == 0 {
546 return Err(invalid("height", "greater than 0"));
547 }
548 Ok(())
549}
550
551#[cfg(test)]
556mod tests {
557 use super::*;
558 use std::collections::HashMap;
559
560 use crate::primitives::private_key::PrivateKey;
561 use crate::wallet::types::{
562 BooleanDefaultFalse, BooleanDefaultTrue, Counterparty, CounterpartyType, Protocol,
563 };
564
565 fn proto(name: &str) -> Protocol {
578 Protocol {
579 security_level: 2,
580 protocol: name.to_string(),
581 }
582 }
583
584 #[test]
586 fn payment_request_auth_is_a_valid_protocol() {
587 validate_protocol_id(&proto("payment request auth"))
588 .expect("the wire protocol every other SDK speaks must validate here too");
589 }
590
591 #[test]
593 fn protocol_names_beginning_with_the_letter_p_are_allowed() {
594 for name in ["policy", "presign", "payments", "paragon vault"] {
595 validate_protocol_id(&proto(name)).unwrap_or_else(|e| {
596 panic!("{name:?} must be a valid protocol name, got: {e}");
597 });
598 }
599 }
600
601 #[test]
603 fn the_reserved_p_namespace_is_still_rejected() {
604 assert!(validate_protocol_id(&proto("p foo")).is_err());
605 assert!(validate_protocol_id(&proto("p 1 bar")).is_err());
606 }
607
608 #[test]
610 fn basket_names_beginning_with_the_letter_p_are_allowed() {
611 for name in ["payments", "presigs", "paragon tokens"] {
612 validate_basket_name(name)
613 .unwrap_or_else(|e| panic!("{name:?} must be a valid basket, got: {e}"));
614 }
615 assert!(validate_basket_name("p foo").is_err());
617 }
618
619 fn test_pubkey() -> crate::primitives::public_key::PublicKey {
620 let pk = PrivateKey::from_bytes(&{
621 let mut buf = [0u8; 32];
622 buf[31] = 42;
623 buf
624 })
625 .unwrap();
626 pk.to_public_key()
627 }
628
629 fn test_counterparty() -> Counterparty {
630 Counterparty {
631 counterparty_type: CounterpartyType::Other,
632 public_key: Some(test_pubkey()),
633 }
634 }
635
636 fn test_protocol() -> Protocol {
637 Protocol {
638 security_level: 1,
639 protocol: "test signing".to_string(),
640 }
641 }
642
643 #[test]
646 fn test_create_action_valid() {
647 let args = CreateActionArgs {
648 description: "Valid description text".to_string(),
649 input_beef: None,
650 inputs: vec![],
651 outputs: vec![],
652 lock_time: None,
653 version: None,
654 labels: vec![],
655 options: None,
656 reference: None,
657 };
658 assert!(validate_create_action_args(&args).is_ok());
659 }
660
661 #[test]
662 fn test_create_action_short_description() {
663 let args = CreateActionArgs {
664 description: "Hi".to_string(),
665 input_beef: None,
666 inputs: vec![],
667 outputs: vec![],
668 lock_time: None,
669 version: None,
670 labels: vec![],
671 options: None,
672 reference: None,
673 };
674 assert!(validate_create_action_args(&args).is_err());
675 }
676
677 #[test]
678 fn test_create_action_label_too_long() {
679 let args = CreateActionArgs {
680 description: "Valid description text".to_string(),
681 input_beef: None,
682 inputs: vec![],
683 outputs: vec![],
684 lock_time: None,
685 version: None,
686 labels: vec!["x".repeat(301)],
687 options: None,
688 reference: None,
689 };
690 assert!(validate_create_action_args(&args).is_err());
691 }
692
693 #[test]
694 fn test_create_action_input_needs_script_or_length() {
695 let args = CreateActionArgs {
696 description: "Valid description text".to_string(),
697 input_beef: None,
698 inputs: vec![CreateActionInput {
699 outpoint: "abc.0".to_string(),
700 input_description: "test input".to_string(),
701 unlocking_script: None,
702 unlocking_script_length: None,
703 sequence_number: None,
704 }],
705 outputs: vec![],
706 lock_time: None,
707 version: None,
708 labels: vec![],
709 options: None,
710 reference: None,
711 };
712 assert!(validate_create_action_args(&args).is_err());
713 }
714
715 #[test]
718 fn test_sign_action_valid() {
719 let mut spends = HashMap::new();
720 spends.insert(
721 0,
722 SignActionSpend {
723 unlocking_script: vec![1, 2, 3],
724 sequence_number: None,
725 },
726 );
727 let args = SignActionArgs {
728 reference: vec![1, 2, 3],
729 spends,
730 options: None,
731 };
732 assert!(validate_sign_action_args(&args).is_ok());
733 }
734
735 #[test]
736 fn test_sign_action_empty_reference() {
737 let mut spends = HashMap::new();
738 spends.insert(
739 0,
740 SignActionSpend {
741 unlocking_script: vec![1],
742 sequence_number: None,
743 },
744 );
745 let args = SignActionArgs {
746 reference: vec![],
747 spends,
748 options: None,
749 };
750 assert!(validate_sign_action_args(&args).is_err());
751 }
752
753 #[test]
754 fn test_sign_action_empty_spends() {
755 let args = SignActionArgs {
756 reference: vec![1, 2, 3],
757 spends: HashMap::new(),
758 options: None,
759 };
760 assert!(validate_sign_action_args(&args).is_err());
761 }
762
763 #[test]
764 fn test_sign_action_empty_unlocking_script() {
765 let mut spends = HashMap::new();
766 spends.insert(
767 0,
768 SignActionSpend {
769 unlocking_script: vec![],
770 sequence_number: None,
771 },
772 );
773 let args = SignActionArgs {
774 reference: vec![1, 2, 3],
775 spends,
776 options: None,
777 };
778 assert!(validate_sign_action_args(&args).is_err());
779 }
780
781 #[test]
784 fn test_abort_action_valid() {
785 let args = AbortActionArgs {
786 reference: vec![1, 2, 3],
787 };
788 assert!(validate_abort_action_args(&args).is_ok());
789 }
790
791 #[test]
792 fn test_abort_action_empty_reference() {
793 let args = AbortActionArgs { reference: vec![] };
794 assert!(validate_abort_action_args(&args).is_err());
795 }
796
797 #[test]
800 fn test_list_actions_valid() {
801 let args = ListActionsArgs {
802 labels: vec!["test".to_string()],
803 label_query_mode: None,
804 include_labels: BooleanDefaultFalse(None),
805 include_inputs: BooleanDefaultFalse(None),
806 include_input_source_locking_scripts: BooleanDefaultFalse(None),
807 include_input_unlocking_scripts: BooleanDefaultFalse(None),
808 include_outputs: BooleanDefaultFalse(None),
809 include_output_locking_scripts: BooleanDefaultFalse(None),
810 limit: Some(10),
811 offset: None,
812 seek_permission: BooleanDefaultTrue(None),
813 };
814 assert!(validate_list_actions_args(&args).is_ok());
815 }
816
817 #[test]
818 fn test_list_actions_empty_labels() {
819 let args = ListActionsArgs {
820 labels: vec![],
821 label_query_mode: None,
822 include_labels: BooleanDefaultFalse(None),
823 include_inputs: BooleanDefaultFalse(None),
824 include_input_source_locking_scripts: BooleanDefaultFalse(None),
825 include_input_unlocking_scripts: BooleanDefaultFalse(None),
826 include_outputs: BooleanDefaultFalse(None),
827 include_output_locking_scripts: BooleanDefaultFalse(None),
828 limit: None,
829 offset: None,
830 seek_permission: BooleanDefaultTrue(None),
831 };
832 assert!(validate_list_actions_args(&args).is_err());
833 }
834
835 #[test]
836 fn test_list_actions_limit_too_high() {
837 let args = ListActionsArgs {
838 labels: vec!["test".to_string()],
839 label_query_mode: None,
840 include_labels: BooleanDefaultFalse(None),
841 include_inputs: BooleanDefaultFalse(None),
842 include_input_source_locking_scripts: BooleanDefaultFalse(None),
843 include_input_unlocking_scripts: BooleanDefaultFalse(None),
844 include_outputs: BooleanDefaultFalse(None),
845 include_output_locking_scripts: BooleanDefaultFalse(None),
846 limit: Some(10001),
847 offset: None,
848 seek_permission: BooleanDefaultTrue(None),
849 };
850 assert!(validate_list_actions_args(&args).is_err());
851 }
852
853 #[test]
854 fn test_list_actions_limit_zero() {
855 let args = ListActionsArgs {
856 labels: vec!["test".to_string()],
857 label_query_mode: None,
858 include_labels: BooleanDefaultFalse(None),
859 include_inputs: BooleanDefaultFalse(None),
860 include_input_source_locking_scripts: BooleanDefaultFalse(None),
861 include_input_unlocking_scripts: BooleanDefaultFalse(None),
862 include_outputs: BooleanDefaultFalse(None),
863 include_output_locking_scripts: BooleanDefaultFalse(None),
864 limit: Some(0),
865 offset: None,
866 seek_permission: BooleanDefaultTrue(None),
867 };
868 assert!(validate_list_actions_args(&args).is_err());
869 }
870
871 #[test]
874 fn test_internalize_action_valid() {
875 let args = InternalizeActionArgs {
876 tx: vec![1, 2, 3],
877 description: "Valid description text".to_string(),
878 labels: vec![],
879 seek_permission: BooleanDefaultTrue(None),
880 outputs: vec![InternalizeOutput::BasketInsertion {
881 output_index: 0,
882 insertion: BasketInsertion {
883 basket: "test-basket".to_string(),
884 custom_instructions: None,
885 tags: vec![],
886 },
887 }],
888 };
889 assert!(validate_internalize_action_args(&args).is_ok());
890 }
891
892 #[test]
893 fn test_internalize_action_empty_tx() {
894 let args = InternalizeActionArgs {
895 tx: vec![],
896 description: "Valid description text".to_string(),
897 labels: vec![],
898 seek_permission: BooleanDefaultTrue(None),
899 outputs: vec![InternalizeOutput::BasketInsertion {
900 output_index: 0,
901 insertion: BasketInsertion {
902 basket: "test".to_string(),
903 custom_instructions: None,
904 tags: vec![],
905 },
906 }],
907 };
908 assert!(validate_internalize_action_args(&args).is_err());
909 }
910
911 #[test]
912 fn test_internalize_action_empty_outputs() {
913 let args = InternalizeActionArgs {
914 tx: vec![1, 2, 3],
915 description: "Valid description text".to_string(),
916 labels: vec![],
917 seek_permission: BooleanDefaultTrue(None),
918 outputs: vec![],
919 };
920 assert!(validate_internalize_action_args(&args).is_err());
921 }
922
923 #[test]
926 fn test_list_outputs_valid() {
927 let args = ListOutputsArgs {
928 basket: "token store".to_string(),
929 tags: vec![],
930 tag_query_mode: None,
931 include: None,
932 include_custom_instructions: BooleanDefaultFalse(None),
933 include_tags: BooleanDefaultFalse(None),
934 include_labels: BooleanDefaultFalse(None),
935 limit: Some(10),
936 offset: None,
937 seek_permission: BooleanDefaultTrue(None),
938 };
939 assert!(validate_list_outputs_args(&args).is_ok());
940 }
941
942 #[test]
943 fn test_list_outputs_empty_basket() {
944 let args = ListOutputsArgs {
945 basket: "".to_string(),
946 tags: vec![],
947 tag_query_mode: None,
948 include: None,
949 include_custom_instructions: BooleanDefaultFalse(None),
950 include_tags: BooleanDefaultFalse(None),
951 include_labels: BooleanDefaultFalse(None),
952 limit: None,
953 offset: None,
954 seek_permission: BooleanDefaultTrue(None),
955 };
956 assert!(validate_list_outputs_args(&args).is_err());
957 }
958
959 #[test]
960 fn test_list_outputs_limit_too_high() {
961 let args = ListOutputsArgs {
962 basket: "token store".to_string(),
963 tags: vec![],
964 tag_query_mode: None,
965 include: None,
966 include_custom_instructions: BooleanDefaultFalse(None),
967 include_tags: BooleanDefaultFalse(None),
968 include_labels: BooleanDefaultFalse(None),
969 limit: Some(10001),
970 offset: None,
971 seek_permission: BooleanDefaultTrue(None),
972 };
973 assert!(validate_list_outputs_args(&args).is_err());
974 }
975
976 #[test]
979 fn test_relinquish_output_valid() {
980 let args = RelinquishOutputArgs {
981 basket: "token store".to_string(),
982 output: "abc123.0".to_string(),
983 };
984 assert!(validate_relinquish_output_args(&args).is_ok());
985 }
986
987 #[test]
988 fn test_relinquish_output_empty_basket() {
989 let args = RelinquishOutputArgs {
990 basket: "".to_string(),
991 output: "abc123.0".to_string(),
992 };
993 assert!(validate_relinquish_output_args(&args).is_err());
994 }
995
996 #[test]
997 fn test_relinquish_output_empty_output() {
998 let args = RelinquishOutputArgs {
999 basket: "token store".to_string(),
1000 output: "".to_string(),
1001 };
1002 assert!(validate_relinquish_output_args(&args).is_err());
1003 }
1004
1005 #[test]
1008 fn test_get_public_key_identity() {
1009 let args = GetPublicKeyArgs {
1010 identity_key: true,
1011 protocol_id: None,
1012 key_id: None,
1013 counterparty: None,
1014 privileged: false,
1015 privileged_reason: None,
1016 for_self: None,
1017 seek_permission: None,
1018 };
1019 assert!(validate_get_public_key_args(&args).is_ok());
1020 }
1021
1022 #[test]
1023 fn test_get_public_key_derived_valid() {
1024 let args = GetPublicKeyArgs {
1025 identity_key: false,
1026 protocol_id: Some(test_protocol()),
1027 key_id: Some("my-key".to_string()),
1028 counterparty: Some(test_counterparty()),
1029 privileged: false,
1030 privileged_reason: None,
1031 for_self: None,
1032 seek_permission: None,
1033 };
1034 assert!(validate_get_public_key_args(&args).is_ok());
1035 }
1036
1037 #[test]
1038 fn test_get_public_key_derived_missing_protocol() {
1039 let args = GetPublicKeyArgs {
1040 identity_key: false,
1041 protocol_id: None,
1042 key_id: Some("my-key".to_string()),
1043 counterparty: None,
1044 privileged: false,
1045 privileged_reason: None,
1046 for_self: None,
1047 seek_permission: None,
1048 };
1049 assert!(validate_get_public_key_args(&args).is_err());
1050 }
1051
1052 #[test]
1053 fn test_get_public_key_derived_missing_key_id() {
1054 let args = GetPublicKeyArgs {
1055 identity_key: false,
1056 protocol_id: Some(test_protocol()),
1057 key_id: None,
1058 counterparty: None,
1059 privileged: false,
1060 privileged_reason: None,
1061 for_self: None,
1062 seek_permission: None,
1063 };
1064 assert!(validate_get_public_key_args(&args).is_err());
1065 }
1066
1067 #[test]
1070 fn test_encrypt_valid() {
1071 let args = EncryptArgs {
1072 protocol_id: test_protocol(),
1073 key_id: "my-key".to_string(),
1074 counterparty: test_counterparty(),
1075 plaintext: vec![1, 2, 3],
1076 privileged: false,
1077 privileged_reason: None,
1078 seek_permission: None,
1079 };
1080 assert!(validate_encrypt_args(&args).is_ok());
1081 }
1082
1083 #[test]
1084 fn test_encrypt_empty_plaintext() {
1085 let args = EncryptArgs {
1086 protocol_id: test_protocol(),
1087 key_id: "my-key".to_string(),
1088 counterparty: test_counterparty(),
1089 plaintext: vec![],
1090 privileged: false,
1091 privileged_reason: None,
1092 seek_permission: None,
1093 };
1094 assert!(validate_encrypt_args(&args).is_err());
1095 }
1096
1097 #[test]
1098 fn test_encrypt_empty_protocol() {
1099 let args = EncryptArgs {
1100 protocol_id: Protocol {
1101 security_level: 1,
1102 protocol: "".to_string(),
1103 },
1104 key_id: "my-key".to_string(),
1105 counterparty: test_counterparty(),
1106 plaintext: vec![1, 2, 3],
1107 privileged: false,
1108 privileged_reason: None,
1109 seek_permission: None,
1110 };
1111 assert!(validate_encrypt_args(&args).is_err());
1112 }
1113
1114 #[test]
1115 fn test_encrypt_empty_key_id() {
1116 let args = EncryptArgs {
1117 protocol_id: test_protocol(),
1118 key_id: "".to_string(),
1119 counterparty: test_counterparty(),
1120 plaintext: vec![1, 2, 3],
1121 privileged: false,
1122 privileged_reason: None,
1123 seek_permission: None,
1124 };
1125 assert!(validate_encrypt_args(&args).is_err());
1126 }
1127
1128 #[test]
1129 fn test_encrypt_invalid_security_level() {
1130 let args = EncryptArgs {
1131 protocol_id: Protocol {
1132 security_level: 5,
1133 protocol: "test".to_string(),
1134 },
1135 key_id: "my-key".to_string(),
1136 counterparty: test_counterparty(),
1137 plaintext: vec![1, 2, 3],
1138 privileged: false,
1139 privileged_reason: None,
1140 seek_permission: None,
1141 };
1142 assert!(validate_encrypt_args(&args).is_err());
1143 }
1144
1145 #[test]
1148 fn test_decrypt_valid() {
1149 let args = DecryptArgs {
1150 protocol_id: test_protocol(),
1151 key_id: "my-key".to_string(),
1152 counterparty: test_counterparty(),
1153 ciphertext: vec![1, 2, 3],
1154 privileged: false,
1155 privileged_reason: None,
1156 seek_permission: None,
1157 };
1158 assert!(validate_decrypt_args(&args).is_ok());
1159 }
1160
1161 #[test]
1162 fn test_decrypt_empty_ciphertext() {
1163 let args = DecryptArgs {
1164 protocol_id: test_protocol(),
1165 key_id: "my-key".to_string(),
1166 counterparty: test_counterparty(),
1167 ciphertext: vec![],
1168 privileged: false,
1169 privileged_reason: None,
1170 seek_permission: None,
1171 };
1172 assert!(validate_decrypt_args(&args).is_err());
1173 }
1174
1175 #[test]
1178 fn test_create_hmac_valid() {
1179 let args = CreateHmacArgs {
1180 protocol_id: test_protocol(),
1181 key_id: "my-key".to_string(),
1182 counterparty: test_counterparty(),
1183 data: vec![1, 2, 3],
1184 privileged: false,
1185 privileged_reason: None,
1186 seek_permission: None,
1187 };
1188 assert!(validate_create_hmac_args(&args).is_ok());
1189 }
1190
1191 #[test]
1192 fn test_create_hmac_empty_data() {
1193 let args = CreateHmacArgs {
1194 protocol_id: test_protocol(),
1195 key_id: "my-key".to_string(),
1196 counterparty: test_counterparty(),
1197 data: vec![],
1198 privileged: false,
1199 privileged_reason: None,
1200 seek_permission: None,
1201 };
1202 assert!(validate_create_hmac_args(&args).is_err());
1203 }
1204
1205 #[test]
1208 fn test_verify_hmac_valid() {
1209 let args = VerifyHmacArgs {
1210 protocol_id: test_protocol(),
1211 key_id: "my-key".to_string(),
1212 counterparty: test_counterparty(),
1213 data: vec![1, 2, 3],
1214 hmac: vec![4, 5, 6],
1215 privileged: false,
1216 privileged_reason: None,
1217 seek_permission: None,
1218 };
1219 assert!(validate_verify_hmac_args(&args).is_ok());
1220 }
1221
1222 #[test]
1223 fn test_verify_hmac_empty_hmac() {
1224 let args = VerifyHmacArgs {
1225 protocol_id: test_protocol(),
1226 key_id: "my-key".to_string(),
1227 counterparty: test_counterparty(),
1228 data: vec![1, 2, 3],
1229 hmac: vec![],
1230 privileged: false,
1231 privileged_reason: None,
1232 seek_permission: None,
1233 };
1234 assert!(validate_verify_hmac_args(&args).is_err());
1235 }
1236
1237 #[test]
1240 fn test_create_signature_valid() {
1241 let args = CreateSignatureArgs {
1242 protocol_id: test_protocol(),
1243 key_id: "my-key".to_string(),
1244 counterparty: test_counterparty(),
1245 data: Some(vec![1, 2, 3]),
1246 hash_to_directly_sign: None,
1247 privileged: false,
1248 privileged_reason: None,
1249 seek_permission: None,
1250 };
1251 assert!(validate_create_signature_args(&args).is_ok());
1252 }
1253
1254 #[test]
1255 fn test_create_signature_empty_data() {
1256 let args = CreateSignatureArgs {
1257 protocol_id: test_protocol(),
1258 key_id: "my-key".to_string(),
1259 counterparty: test_counterparty(),
1260 data: Some(vec![]),
1261 hash_to_directly_sign: None,
1262 privileged: false,
1263 privileged_reason: None,
1264 seek_permission: None,
1265 };
1266 assert!(validate_create_signature_args(&args).is_err());
1267 }
1268
1269 #[test]
1272 fn test_verify_signature_valid() {
1273 let args = VerifySignatureArgs {
1274 protocol_id: test_protocol(),
1275 key_id: "my-key".to_string(),
1276 counterparty: test_counterparty(),
1277 data: Some(vec![1, 2, 3]),
1278 hash_to_directly_verify: None,
1279 signature: vec![4, 5, 6],
1280 for_self: None,
1281 privileged: false,
1282 privileged_reason: None,
1283 seek_permission: None,
1284 };
1285 assert!(validate_verify_signature_args(&args).is_ok());
1286 }
1287
1288 #[test]
1289 fn test_verify_signature_empty_signature() {
1290 let args = VerifySignatureArgs {
1291 protocol_id: test_protocol(),
1292 key_id: "my-key".to_string(),
1293 counterparty: test_counterparty(),
1294 data: Some(vec![1, 2, 3]),
1295 hash_to_directly_verify: None,
1296 signature: vec![],
1297 for_self: None,
1298 privileged: false,
1299 privileged_reason: None,
1300 seek_permission: None,
1301 };
1302 assert!(validate_verify_signature_args(&args).is_err());
1303 }
1304
1305 #[test]
1308 fn test_acquire_certificate_direct_valid() {
1309 let args = AcquireCertificateArgs {
1310 cert_type: CertificateType([0u8; 32]),
1311 certifier: test_pubkey(),
1312 acquisition_protocol: AcquisitionProtocol::Direct,
1313 fields: HashMap::new(),
1314 serial_number: Some(SerialNumber([0u8; 32])),
1315 revocation_outpoint: Some("abc.0".to_string()),
1316 signature: Some(vec![1, 2, 3]),
1317 certifier_url: None,
1318 keyring_revealer: Some(KeyringRevealer::Certifier),
1319 keyring_for_subject: Some(HashMap::new()),
1320 privileged: false,
1321 privileged_reason: None,
1322 };
1323 assert!(validate_acquire_certificate_args(&args).is_ok());
1324 }
1325
1326 #[test]
1327 fn test_acquire_certificate_direct_missing_serial() {
1328 let args = AcquireCertificateArgs {
1329 cert_type: CertificateType([0u8; 32]),
1330 certifier: test_pubkey(),
1331 acquisition_protocol: AcquisitionProtocol::Direct,
1332 fields: HashMap::new(),
1333 serial_number: None,
1334 revocation_outpoint: Some("abc.0".to_string()),
1335 signature: Some(vec![1, 2, 3]),
1336 certifier_url: None,
1337 keyring_revealer: Some(KeyringRevealer::Certifier),
1338 keyring_for_subject: Some(HashMap::new()),
1339 privileged: false,
1340 privileged_reason: None,
1341 };
1342 assert!(validate_acquire_certificate_args(&args).is_err());
1343 }
1344
1345 #[test]
1346 fn test_acquire_certificate_issuance_valid() {
1347 let args = AcquireCertificateArgs {
1348 cert_type: CertificateType([0u8; 32]),
1349 certifier: test_pubkey(),
1350 acquisition_protocol: AcquisitionProtocol::Issuance,
1351 fields: HashMap::new(),
1352 serial_number: None,
1353 revocation_outpoint: None,
1354 signature: None,
1355 certifier_url: Some("https://example.com".to_string()),
1356 keyring_revealer: None,
1357 keyring_for_subject: None,
1358 privileged: false,
1359 privileged_reason: None,
1360 };
1361 assert!(validate_acquire_certificate_args(&args).is_ok());
1362 }
1363
1364 #[test]
1365 fn test_acquire_certificate_issuance_missing_url() {
1366 let args = AcquireCertificateArgs {
1367 cert_type: CertificateType([0u8; 32]),
1368 certifier: test_pubkey(),
1369 acquisition_protocol: AcquisitionProtocol::Issuance,
1370 fields: HashMap::new(),
1371 serial_number: None,
1372 revocation_outpoint: None,
1373 signature: None,
1374 certifier_url: None,
1375 keyring_revealer: None,
1376 keyring_for_subject: None,
1377 privileged: false,
1378 privileged_reason: None,
1379 };
1380 assert!(validate_acquire_certificate_args(&args).is_err());
1381 }
1382
1383 #[test]
1386 fn test_list_certificates_valid() {
1387 let args = ListCertificatesArgs {
1388 certifiers: vec![],
1389 types: vec![],
1390 limit: Some(10),
1391 offset: None,
1392 privileged: BooleanDefaultFalse(None),
1393 privileged_reason: None,
1394 partial: None,
1395 };
1396 assert!(validate_list_certificates_args(&args).is_ok());
1397 }
1398
1399 #[test]
1400 fn test_list_certificates_limit_too_high() {
1401 let args = ListCertificatesArgs {
1402 certifiers: vec![],
1403 types: vec![],
1404 limit: Some(10001),
1405 offset: None,
1406 privileged: BooleanDefaultFalse(None),
1407 privileged_reason: None,
1408 partial: None,
1409 };
1410 assert!(validate_list_certificates_args(&args).is_err());
1411 }
1412
1413 #[test]
1416 fn test_prove_certificate_valid() {
1417 let args = ProveCertificateArgs {
1418 certificate: Certificate {
1419 cert_type: CertificateType([0u8; 32]),
1420 serial_number: SerialNumber([0u8; 32]),
1421 subject: test_pubkey(),
1422 certifier: test_pubkey(),
1423 revocation_outpoint: None,
1424 fields: None,
1425 signature: None,
1426 }
1427 .into(),
1428 fields_to_reveal: vec!["name".to_string()],
1429 verifier: test_pubkey(),
1430 privileged: BooleanDefaultFalse(None),
1431 privileged_reason: None,
1432 };
1433 assert!(validate_prove_certificate_args(&args).is_ok());
1434 }
1435
1436 #[test]
1437 fn test_prove_certificate_empty_fields() {
1438 let args = ProveCertificateArgs {
1439 certificate: Certificate {
1440 cert_type: CertificateType([0u8; 32]),
1441 serial_number: SerialNumber([0u8; 32]),
1442 subject: test_pubkey(),
1443 certifier: test_pubkey(),
1444 revocation_outpoint: None,
1445 fields: None,
1446 signature: None,
1447 }
1448 .into(),
1449 fields_to_reveal: vec![],
1450 verifier: test_pubkey(),
1451 privileged: BooleanDefaultFalse(None),
1452 privileged_reason: None,
1453 };
1454 assert!(validate_prove_certificate_args(&args).is_err());
1455 }
1456
1457 #[test]
1460 fn test_relinquish_certificate_valid() {
1461 let args = RelinquishCertificateArgs {
1462 cert_type: CertificateType([0u8; 32]),
1463 serial_number: SerialNumber([0u8; 32]),
1464 certifier: test_pubkey(),
1465 };
1466 assert!(validate_relinquish_certificate_args(&args).is_ok());
1467 }
1468
1469 #[test]
1472 fn test_discover_by_identity_key_valid() {
1473 let args = DiscoverByIdentityKeyArgs {
1474 identity_key: test_pubkey(),
1475 limit: None,
1476 offset: None,
1477 seek_permission: None,
1478 };
1479 assert!(validate_discover_by_identity_key_args(&args).is_ok());
1480 }
1481
1482 #[test]
1485 fn test_discover_by_attributes_valid() {
1486 let mut attrs = HashMap::new();
1487 attrs.insert("name".to_string(), "Alice".to_string());
1488 let args = DiscoverByAttributesArgs {
1489 attributes: attrs,
1490 limit: None,
1491 offset: None,
1492 seek_permission: None,
1493 };
1494 assert!(validate_discover_by_attributes_args(&args).is_ok());
1495 }
1496
1497 #[test]
1498 fn test_discover_by_attributes_empty() {
1499 let args = DiscoverByAttributesArgs {
1500 attributes: HashMap::new(),
1501 limit: None,
1502 offset: None,
1503 seek_permission: None,
1504 };
1505 assert!(validate_discover_by_attributes_args(&args).is_err());
1506 }
1507
1508 #[test]
1511 fn test_reveal_counterparty_key_linkage_valid() {
1512 let args = RevealCounterpartyKeyLinkageArgs {
1513 counterparty: test_pubkey(),
1514 verifier: test_pubkey(),
1515 privileged: None,
1516 privileged_reason: None,
1517 };
1518 assert!(validate_reveal_counterparty_key_linkage_args(&args).is_ok());
1519 }
1520
1521 #[test]
1524 fn test_reveal_specific_key_linkage_valid() {
1525 let args = RevealSpecificKeyLinkageArgs {
1526 counterparty: test_counterparty(),
1527 verifier: test_pubkey(),
1528 protocol_id: test_protocol(),
1529 key_id: "my-key".to_string(),
1530 privileged: None,
1531 privileged_reason: None,
1532 };
1533 assert!(validate_reveal_specific_key_linkage_args(&args).is_ok());
1534 }
1535
1536 #[test]
1537 fn test_reveal_specific_key_linkage_empty_key_id() {
1538 let args = RevealSpecificKeyLinkageArgs {
1539 counterparty: test_counterparty(),
1540 verifier: test_pubkey(),
1541 protocol_id: test_protocol(),
1542 key_id: "".to_string(),
1543 privileged: None,
1544 privileged_reason: None,
1545 };
1546 assert!(validate_reveal_specific_key_linkage_args(&args).is_err());
1547 }
1548
1549 #[test]
1552 fn test_get_header_valid() {
1553 let args = GetHeaderArgs { height: 100 };
1554 assert!(validate_get_header_args(&args).is_ok());
1555 }
1556
1557 #[test]
1558 fn test_get_header_zero_height() {
1559 let args = GetHeaderArgs { height: 0 };
1560 assert!(validate_get_header_args(&args).is_err());
1561 }
1562
1563 #[test]
1566 fn test_privileged_without_reason() {
1567 let args = EncryptArgs {
1568 protocol_id: test_protocol(),
1569 key_id: "my-key".to_string(),
1570 counterparty: test_counterparty(),
1571 plaintext: vec![1, 2, 3],
1572 privileged: true,
1573 privileged_reason: None,
1574 seek_permission: None,
1575 };
1576 assert!(validate_encrypt_args(&args).is_err());
1577 }
1578
1579 #[test]
1580 fn test_privileged_with_short_reason() {
1581 let args = EncryptArgs {
1582 protocol_id: test_protocol(),
1583 key_id: "my-key".to_string(),
1584 counterparty: test_counterparty(),
1585 plaintext: vec![1, 2, 3],
1586 privileged: true,
1587 privileged_reason: Some("ab".to_string()),
1588 seek_permission: None,
1589 };
1590 assert!(validate_encrypt_args(&args).is_err());
1591 }
1592
1593 #[test]
1594 fn test_privileged_with_valid_reason() {
1595 let args = EncryptArgs {
1596 protocol_id: test_protocol(),
1597 key_id: "my-key".to_string(),
1598 counterparty: test_counterparty(),
1599 plaintext: vec![1, 2, 3],
1600 privileged: true,
1601 privileged_reason: Some("Admin access required".to_string()),
1602 seek_permission: None,
1603 };
1604 assert!(validate_encrypt_args(&args).is_ok());
1605 }
1606}