1use serde::ser::SerializeMap;
7use serde::{Deserialize, Serialize, Serializer};
8use std::fmt;
9
10use crate::capability::Capability;
11use crate::types::{CesrKey, ConfigTrait, Prefix, Said, Threshold, VersionString};
12
13pub const KERI_VERSION_PREFIX: &str = "KERI10JSON";
15
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct KeriSequence(u128);
30
31#[cfg(feature = "schema")]
32impl schemars::JsonSchema for KeriSequence {
33 fn schema_name() -> String {
34 "KeriSequence".to_string()
35 }
36
37 fn json_schema(_gen: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
38 schemars::schema::SchemaObject {
39 instance_type: Some(schemars::schema::InstanceType::String.into()),
40 ..Default::default()
41 }
42 .into()
43 }
44}
45
46impl KeriSequence {
47 pub fn new(value: u128) -> Self {
49 Self(value)
50 }
51
52 pub fn value(self) -> u128 {
54 self.0
55 }
56}
57
58impl fmt::Display for KeriSequence {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(f, "{:x}", self.0)
61 }
62}
63
64impl Serialize for KeriSequence {
65 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
66 serializer.serialize_str(&format!("{:x}", self.0))
67 }
68}
69
70impl<'de> Deserialize<'de> for KeriSequence {
71 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
72 let s = String::deserialize(deserializer)?;
73 let value = u128::from_str_radix(&s, 16)
74 .map_err(|_| serde::de::Error::custom(format!("invalid hex sequence: {s:?}")))?;
75 Ok(KeriSequence(value))
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
93#[non_exhaustive]
94pub enum Seal {
95 Digest {
97 d: Said,
99 },
100 SourceEvent {
102 s: KeriSequence,
104 d: Said,
106 },
107 KeyEvent {
109 i: Prefix,
111 s: KeriSequence,
113 d: Said,
115 },
116 EventLocation {
118 i: Prefix,
120 s: KeriSequence,
122 p: Said,
124 t: String,
126 d: Said,
128 },
129 LatestEstablishment {
131 i: Prefix,
133 },
134 #[cfg(feature = "seal-extensions")]
136 MerkleRoot {
137 rd: Said,
139 },
140 #[cfg(feature = "seal-extensions")]
142 RegistrarBacker {
143 bi: Prefix,
145 d: Said,
147 },
148}
149
150impl Seal {
151 pub fn digest(said: impl Into<String>) -> Self {
153 Self::Digest {
154 d: Said::new_unchecked(said.into()),
155 }
156 }
157
158 pub fn key_event(prefix: Prefix, sequence: KeriSequence, said: Said) -> Self {
160 Self::KeyEvent {
161 i: prefix,
162 s: sequence,
163 d: said,
164 }
165 }
166
167 pub fn digest_value(&self) -> Option<&Said> {
169 match self {
170 Seal::Digest { d } => Some(d),
171 Seal::SourceEvent { d, .. } => Some(d),
172 Seal::KeyEvent { d, .. } => Some(d),
173 Seal::EventLocation { d, .. } => Some(d),
174 Seal::LatestEstablishment { .. } => None,
175 #[cfg(feature = "seal-extensions")]
176 Seal::RegistrarBacker { d, .. } => Some(d),
177 #[cfg(feature = "seal-extensions")]
178 Seal::MerkleRoot { rd } => Some(rd),
179 }
180 }
181}
182
183impl Serialize for Seal {
184 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
185 match self {
186 Seal::Digest { d } => {
187 let mut map = serializer.serialize_map(Some(1))?;
188 map.serialize_entry("d", d)?;
189 map.end()
190 }
191 Seal::SourceEvent { s, d } => {
192 let mut map = serializer.serialize_map(Some(2))?;
193 map.serialize_entry("s", s)?;
194 map.serialize_entry("d", d)?;
195 map.end()
196 }
197 Seal::KeyEvent { i, s, d } => {
198 let mut map = serializer.serialize_map(Some(3))?;
199 map.serialize_entry("i", i)?;
200 map.serialize_entry("s", s)?;
201 map.serialize_entry("d", d)?;
202 map.end()
203 }
204 Seal::EventLocation { i, s, p, t, d } => {
205 let mut map = serializer.serialize_map(Some(5))?;
206 map.serialize_entry("i", i)?;
207 map.serialize_entry("s", s)?;
208 map.serialize_entry("p", p)?;
209 map.serialize_entry("t", t)?;
210 map.serialize_entry("d", d)?;
211 map.end()
212 }
213 Seal::LatestEstablishment { i } => {
214 let mut map = serializer.serialize_map(Some(1))?;
215 map.serialize_entry("i", i)?;
216 map.end()
217 }
218 #[cfg(feature = "seal-extensions")]
219 Seal::MerkleRoot { rd } => {
220 let mut map = serializer.serialize_map(Some(1))?;
221 map.serialize_entry("rd", rd)?;
222 map.end()
223 }
224 #[cfg(feature = "seal-extensions")]
225 Seal::RegistrarBacker { bi, d } => {
226 let mut map = serializer.serialize_map(Some(2))?;
227 map.serialize_entry("bi", bi)?;
228 map.serialize_entry("d", d)?;
229 map.end()
230 }
231 }
232 }
233}
234
235impl<'de> Deserialize<'de> for Seal {
236 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
237 let map: serde_json::Map<String, serde_json::Value> =
238 serde_json::Map::deserialize(deserializer)?;
239
240 let want_str = |k: &str| -> Result<String, D::Error> {
241 map.get(k)
242 .and_then(|v| v.as_str())
243 .map(|v| v.to_string())
244 .ok_or_else(|| {
245 serde::de::Error::custom(format!("seal field `{k}` must be a string"))
246 })
247 };
248 let want_seq =
249 |k: &str| -> Result<KeriSequence, D::Error> {
250 serde_json::from_value(map.get(k).cloned().ok_or_else(|| {
251 serde::de::Error::custom(format!("seal field `{k}` required"))
252 })?)
253 .map_err(serde::de::Error::custom)
254 };
255
256 let mut keys: Vec<&str> = map.keys().map(String::as_str).collect();
258 keys.sort_unstable();
259
260 match keys.as_slice() {
261 ["d"] => Ok(Seal::Digest {
262 d: Said::new_unchecked(want_str("d")?),
263 }),
264 ["d", "s"] => Ok(Seal::SourceEvent {
265 s: want_seq("s")?,
266 d: Said::new_unchecked(want_str("d")?),
267 }),
268 ["d", "i", "s"] => Ok(Seal::KeyEvent {
269 i: Prefix::new_unchecked(want_str("i")?),
270 s: want_seq("s")?,
271 d: Said::new_unchecked(want_str("d")?),
272 }),
273 ["d", "i", "p", "s", "t"] => Ok(Seal::EventLocation {
274 i: Prefix::new_unchecked(want_str("i")?),
275 s: want_seq("s")?,
276 p: Said::new_unchecked(want_str("p")?),
277 t: want_str("t")?,
278 d: Said::new_unchecked(want_str("d")?),
279 }),
280 ["i"] => Ok(Seal::LatestEstablishment {
281 i: Prefix::new_unchecked(want_str("i")?),
282 }),
283 #[cfg(feature = "seal-extensions")]
284 ["rd"] => Ok(Seal::MerkleRoot {
285 rd: Said::new_unchecked(want_str("rd")?),
286 }),
287 #[cfg(feature = "seal-extensions")]
288 ["bi", "d"] => Ok(Seal::RegistrarBacker {
289 bi: Prefix::new_unchecked(want_str("bi")?),
290 d: Said::new_unchecked(want_str("d")?),
291 }),
292 other => Err(serde::de::Error::custom(format!(
293 "unrecognized seal field set: {other:?}"
294 ))),
295 }
296 }
297}
298
299#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
308#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
309pub struct IcpEvent {
310 pub v: VersionString,
312 pub d: Said,
314 pub i: Prefix,
316 pub s: KeriSequence,
318 pub kt: Threshold,
320 pub k: Vec<CesrKey>,
322 pub nt: Threshold,
324 pub n: Vec<Said>,
326 pub bt: Threshold,
328 #[serde(default)]
330 pub b: Vec<Prefix>,
331 #[serde(default)]
333 pub c: Vec<ConfigTrait>,
334 #[serde(default)]
336 pub a: Vec<Seal>,
337}
338
339pub struct IcpEventInit {
345 pub v: VersionString,
347 pub d: Said,
349 pub i: Prefix,
351 pub s: KeriSequence,
353 pub kt: Threshold,
355 pub k: Vec<CesrKey>,
357 pub nt: Threshold,
359 pub n: Vec<Said>,
361 pub bt: Threshold,
363 pub b: Vec<Prefix>,
365 pub c: Vec<ConfigTrait>,
367 pub a: Vec<Seal>,
369}
370
371impl IcpEvent {
372 pub fn new(init: IcpEventInit) -> Self {
374 Self {
375 v: init.v,
376 d: init.d,
377 i: init.i,
378 s: init.s,
379 kt: init.kt,
380 k: init.k,
381 nt: init.nt,
382 n: init.n,
383 bt: init.bt,
384 b: init.b,
385 c: init.c,
386 a: init.a,
387 }
388 }
389}
390
391impl Serialize for IcpEvent {
393 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
394 let field_count = 13;
395 let mut map = serializer.serialize_map(Some(field_count))?;
396 map.serialize_entry("v", &self.v)?;
397 map.serialize_entry("t", "icp")?;
398 map.serialize_entry("d", &self.d)?;
399 map.serialize_entry("i", &self.i)?;
400 map.serialize_entry("s", &self.s)?;
401 map.serialize_entry("kt", &self.kt)?;
402 map.serialize_entry("k", &self.k)?;
403 map.serialize_entry("nt", &self.nt)?;
404 map.serialize_entry("n", &self.n)?;
405 map.serialize_entry("bt", &self.bt)?;
406 map.serialize_entry("b", &self.b)?;
407 map.serialize_entry("c", &self.c)?;
408 map.serialize_entry("a", &self.a)?;
409 map.end()
410 }
411}
412
413#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
417#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
418pub struct RotEvent {
419 pub v: VersionString,
421 pub d: Said,
423 pub i: Prefix,
425 pub s: KeriSequence,
427 pub p: Said,
429 pub kt: Threshold,
431 pub k: Vec<CesrKey>,
433 pub nt: Threshold,
435 pub n: Vec<Said>,
437 pub bt: Threshold,
439 #[serde(default)]
441 pub br: Vec<Prefix>,
442 #[serde(default)]
444 pub ba: Vec<Prefix>,
445 #[serde(default)]
447 pub c: Vec<ConfigTrait>,
448 #[serde(default)]
450 pub a: Vec<Seal>,
451}
452
453pub struct RotEventInit {
456 pub v: VersionString,
458 pub d: Said,
460 pub i: Prefix,
462 pub s: KeriSequence,
464 pub p: Said,
466 pub kt: Threshold,
468 pub k: Vec<CesrKey>,
470 pub nt: Threshold,
472 pub n: Vec<Said>,
474 pub bt: Threshold,
476 pub br: Vec<Prefix>,
478 pub ba: Vec<Prefix>,
480 pub c: Vec<ConfigTrait>,
482 pub a: Vec<Seal>,
484}
485
486impl RotEvent {
487 pub fn new(init: RotEventInit) -> Self {
489 Self {
490 v: init.v,
491 d: init.d,
492 i: init.i,
493 s: init.s,
494 p: init.p,
495 kt: init.kt,
496 k: init.k,
497 nt: init.nt,
498 n: init.n,
499 bt: init.bt,
500 br: init.br,
501 ba: init.ba,
502 c: init.c,
503 a: init.a,
504 }
505 }
506}
507
508impl Serialize for RotEvent {
510 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
511 let field_count = 14;
512 let mut map = serializer.serialize_map(Some(field_count))?;
513 map.serialize_entry("v", &self.v)?;
514 map.serialize_entry("t", "rot")?;
515 map.serialize_entry("d", &self.d)?;
516 map.serialize_entry("i", &self.i)?;
517 map.serialize_entry("s", &self.s)?;
518 map.serialize_entry("p", &self.p)?;
519 map.serialize_entry("kt", &self.kt)?;
520 map.serialize_entry("k", &self.k)?;
521 map.serialize_entry("nt", &self.nt)?;
522 map.serialize_entry("n", &self.n)?;
523 map.serialize_entry("bt", &self.bt)?;
524 map.serialize_entry("br", &self.br)?;
525 map.serialize_entry("ba", &self.ba)?;
526 map.serialize_entry("a", &self.a)?;
527 map.end()
528 }
529}
530
531#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
535#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
536pub struct IxnEvent {
537 pub v: VersionString,
539 pub d: Said,
541 pub i: Prefix,
543 pub s: KeriSequence,
545 pub p: Said,
547 pub a: Vec<Seal>,
549}
550
551pub struct IxnEventInit {
553 pub v: VersionString,
554 pub d: Said,
555 pub i: Prefix,
556 pub s: KeriSequence,
557 pub p: Said,
558 pub a: Vec<Seal>,
559}
560
561impl IxnEvent {
562 pub fn new(init: IxnEventInit) -> Self {
564 Self {
565 v: init.v,
566 d: init.d,
567 i: init.i,
568 s: init.s,
569 p: init.p,
570 a: init.a,
571 }
572 }
573}
574
575impl Serialize for IxnEvent {
577 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
578 let field_count = 7;
579 let mut map = serializer.serialize_map(Some(field_count))?;
580 map.serialize_entry("v", &self.v)?;
581 map.serialize_entry("t", "ixn")?;
582 map.serialize_entry("d", &self.d)?;
583 map.serialize_entry("i", &self.i)?;
584 map.serialize_entry("s", &self.s)?;
585 map.serialize_entry("p", &self.p)?;
586 map.serialize_entry("a", &self.a)?;
587 map.end()
588 }
589}
590
591#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
609#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
610pub struct DipEvent {
611 pub v: VersionString,
613 pub d: Said,
615 pub i: Prefix,
617 pub s: KeriSequence,
619 pub kt: Threshold,
621 pub k: Vec<CesrKey>,
623 pub nt: Threshold,
625 pub n: Vec<Said>,
627 pub bt: Threshold,
629 #[serde(default)]
631 pub b: Vec<Prefix>,
632 #[serde(default)]
634 pub c: Vec<ConfigTrait>,
635 #[serde(default)]
637 pub a: Vec<Seal>,
638 pub di: Prefix,
640 #[serde(skip)]
646 pub source_seal: Option<SourceSeal>,
647}
648
649pub struct DipEventInit {
651 pub v: VersionString,
653 pub d: Said,
655 pub i: Prefix,
657 pub s: KeriSequence,
659 pub kt: Threshold,
661 pub k: Vec<CesrKey>,
663 pub nt: Threshold,
665 pub n: Vec<Said>,
667 pub bt: Threshold,
669 pub b: Vec<Prefix>,
671 pub c: Vec<ConfigTrait>,
673 pub a: Vec<Seal>,
675 pub di: Prefix,
677}
678
679impl DipEvent {
680 pub fn new(init: DipEventInit) -> Self {
682 Self {
683 v: init.v,
684 d: init.d,
685 i: init.i,
686 s: init.s,
687 kt: init.kt,
688 k: init.k,
689 nt: init.nt,
690 n: init.n,
691 bt: init.bt,
692 b: init.b,
693 c: init.c,
694 a: init.a,
695 di: init.di,
696 source_seal: None,
697 }
698 }
699}
700
701impl Serialize for DipEvent {
703 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
704 let field_count = 14;
705 let mut map = serializer.serialize_map(Some(field_count))?;
706 map.serialize_entry("v", &self.v)?;
707 map.serialize_entry("t", "dip")?;
708 map.serialize_entry("d", &self.d)?;
709 map.serialize_entry("i", &self.i)?;
710 map.serialize_entry("s", &self.s)?;
711 map.serialize_entry("kt", &self.kt)?;
712 map.serialize_entry("k", &self.k)?;
713 map.serialize_entry("nt", &self.nt)?;
714 map.serialize_entry("n", &self.n)?;
715 map.serialize_entry("bt", &self.bt)?;
716 map.serialize_entry("b", &self.b)?;
717 map.serialize_entry("c", &self.c)?;
718 map.serialize_entry("a", &self.a)?;
719 map.serialize_entry("di", &self.di)?;
720 map.end()
721 }
722}
723
724#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
730#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
731pub struct DrtEvent {
732 pub v: VersionString,
734 pub d: Said,
736 pub i: Prefix,
738 pub s: KeriSequence,
740 pub p: Said,
742 pub kt: Threshold,
744 pub k: Vec<CesrKey>,
746 pub nt: Threshold,
748 pub n: Vec<Said>,
750 pub bt: Threshold,
752 #[serde(default)]
754 pub br: Vec<Prefix>,
755 #[serde(default)]
757 pub ba: Vec<Prefix>,
758 #[serde(default)]
760 pub c: Vec<ConfigTrait>,
761 #[serde(default)]
763 pub a: Vec<Seal>,
764 pub di: Prefix,
766 #[serde(skip)]
770 pub source_seal: Option<SourceSeal>,
771}
772
773pub struct DrtEventInit {
775 pub v: VersionString,
777 pub d: Said,
779 pub i: Prefix,
781 pub s: KeriSequence,
783 pub p: Said,
785 pub kt: Threshold,
787 pub k: Vec<CesrKey>,
789 pub nt: Threshold,
791 pub n: Vec<Said>,
793 pub bt: Threshold,
795 pub br: Vec<Prefix>,
797 pub ba: Vec<Prefix>,
799 pub c: Vec<ConfigTrait>,
801 pub a: Vec<Seal>,
803 pub di: Prefix,
805}
806
807impl DrtEvent {
808 pub fn new(init: DrtEventInit) -> Self {
810 Self {
811 v: init.v,
812 d: init.d,
813 i: init.i,
814 s: init.s,
815 p: init.p,
816 kt: init.kt,
817 k: init.k,
818 nt: init.nt,
819 n: init.n,
820 bt: init.bt,
821 br: init.br,
822 ba: init.ba,
823 c: init.c,
824 a: init.a,
825 di: init.di,
826 source_seal: None,
827 }
828 }
829}
830
831impl Serialize for DrtEvent {
833 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
834 let field_count = 15;
835 let mut map = serializer.serialize_map(Some(field_count))?;
836 map.serialize_entry("v", &self.v)?;
837 map.serialize_entry("t", "drt")?;
838 map.serialize_entry("d", &self.d)?;
839 map.serialize_entry("i", &self.i)?;
840 map.serialize_entry("s", &self.s)?;
841 map.serialize_entry("p", &self.p)?;
842 map.serialize_entry("kt", &self.kt)?;
843 map.serialize_entry("k", &self.k)?;
844 map.serialize_entry("nt", &self.nt)?;
845 map.serialize_entry("n", &self.n)?;
846 map.serialize_entry("bt", &self.bt)?;
847 map.serialize_entry("br", &self.br)?;
848 map.serialize_entry("ba", &self.ba)?;
849 map.serialize_entry("a", &self.a)?;
850 map.serialize_entry("di", &self.di)?;
851 map.end()
852 }
853}
854
855#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
857#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
858#[serde(tag = "t")]
859pub enum Event {
860 #[serde(rename = "icp")]
862 Icp(IcpEvent),
863 #[serde(rename = "rot")]
865 Rot(RotEvent),
866 #[serde(rename = "ixn")]
868 Ixn(IxnEvent),
869 #[serde(rename = "dip")]
871 Dip(DipEvent),
872 #[serde(rename = "drt")]
874 Drt(DrtEvent),
875}
876
877impl Serialize for Event {
878 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
879 match self {
880 Event::Icp(e) => e.serialize(serializer),
881 Event::Rot(e) => e.serialize(serializer),
882 Event::Ixn(e) => e.serialize(serializer),
883 Event::Dip(e) => e.serialize(serializer),
884 Event::Drt(e) => e.serialize(serializer),
885 }
886 }
887}
888
889impl Event {
890 pub fn said(&self) -> &Said {
892 match self {
893 Event::Icp(e) => &e.d,
894 Event::Rot(e) => &e.d,
895 Event::Ixn(e) => &e.d,
896 Event::Dip(e) => &e.d,
897 Event::Drt(e) => &e.d,
898 }
899 }
900
901 pub fn sequence(&self) -> KeriSequence {
903 match self {
904 Event::Icp(e) => e.s,
905 Event::Rot(e) => e.s,
906 Event::Ixn(e) => e.s,
907 Event::Dip(e) => e.s,
908 Event::Drt(e) => e.s,
909 }
910 }
911
912 pub fn prefix(&self) -> &Prefix {
914 match self {
915 Event::Icp(e) => &e.i,
916 Event::Rot(e) => &e.i,
917 Event::Ixn(e) => &e.i,
918 Event::Dip(e) => &e.i,
919 Event::Drt(e) => &e.i,
920 }
921 }
922
923 pub fn previous(&self) -> Option<&Said> {
925 match self {
926 Event::Icp(_) | Event::Dip(_) => None,
927 Event::Rot(e) => Some(&e.p),
928 Event::Ixn(e) => Some(&e.p),
929 Event::Drt(e) => Some(&e.p),
930 }
931 }
932
933 pub fn keys(&self) -> Option<&[CesrKey]> {
935 match self {
936 Event::Icp(e) => Some(&e.k),
937 Event::Rot(e) => Some(&e.k),
938 Event::Dip(e) => Some(&e.k),
939 Event::Drt(e) => Some(&e.k),
940 Event::Ixn(_) => None,
941 }
942 }
943
944 pub fn next_commitments(&self) -> Option<&[Said]> {
946 match self {
947 Event::Icp(e) => Some(&e.n),
948 Event::Rot(e) => Some(&e.n),
949 Event::Dip(e) => Some(&e.n),
950 Event::Drt(e) => Some(&e.n),
951 Event::Ixn(_) => None,
952 }
953 }
954
955 pub fn anchors(&self) -> &[Seal] {
957 match self {
958 Event::Icp(e) => &e.a,
959 Event::Rot(e) => &e.a,
960 Event::Ixn(e) => &e.a,
961 Event::Dip(e) => &e.a,
962 Event::Drt(e) => &e.a,
963 }
964 }
965
966 pub fn delegator(&self) -> Option<&Prefix> {
968 match self {
969 Event::Dip(e) => Some(&e.di),
970 _ => None,
971 }
972 }
973
974 pub fn is_inception(&self) -> bool {
976 matches!(self, Event::Icp(_) | Event::Dip(_))
977 }
978
979 pub fn is_rotation(&self) -> bool {
981 matches!(self, Event::Rot(_) | Event::Drt(_))
982 }
983
984 pub fn is_interaction(&self) -> bool {
986 matches!(self, Event::Ixn(_))
987 }
988
989 pub fn is_delegated(&self) -> bool {
991 matches!(self, Event::Dip(_) | Event::Drt(_))
992 }
993
994 pub fn source_seal(&self) -> Option<&SourceSeal> {
997 match self {
998 Event::Dip(e) => e.source_seal.as_ref(),
999 Event::Drt(e) => e.source_seal.as_ref(),
1000 _ => None,
1001 }
1002 }
1003}
1004
1005#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1020pub struct IndexedSignature {
1021 pub index: u32,
1023 #[serde(default, skip_serializing_if = "Option::is_none")]
1030 pub prior_index: Option<u32>,
1031 #[serde(with = "hex::serde")]
1033 pub sig: Vec<u8>,
1034}
1035
1036#[derive(Debug, Clone, PartialEq, Eq)]
1045#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1046pub struct SourceSeal {
1047 pub s: KeriSequence,
1049 pub d: Said,
1051}
1052
1053#[derive(Debug, Clone, PartialEq, Eq, Default)]
1062#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1063pub struct AgentScope {
1064 pub capabilities: Vec<Capability>,
1067 pub expires_at: Option<i64>,
1069}
1070
1071const AGENT_SCOPE_MARKER: &str = "agentscope:";
1073
1074pub fn encode_agent_scope(agent_prefix: &str, scope: &AgentScope) -> String {
1090 let expires = scope.expires_at.unwrap_or(0);
1091 let caps = scope
1092 .capabilities
1093 .iter()
1094 .map(Capability::as_str)
1095 .collect::<Vec<_>>()
1096 .join(",");
1097 format!("{AGENT_SCOPE_MARKER}{agent_prefix}:{expires}:{caps}")
1098}
1099
1100pub fn decode_agent_scope(value: &str) -> Option<(String, AgentScope)> {
1107 let rest = value.strip_prefix(AGENT_SCOPE_MARKER)?;
1110 let mut parts = rest.splitn(3, ':');
1111 let prefix = parts.next()?.to_string();
1112 let expires: i64 = parts.next()?.parse().ok()?;
1113 let caps_csv = parts.next().unwrap_or("");
1114 let capabilities = if caps_csv.is_empty() {
1115 Vec::new()
1116 } else {
1117 caps_csv
1118 .split(',')
1119 .map(Capability::parse)
1120 .collect::<Result<Vec<_>, _>>()
1121 .ok()?
1122 };
1123 Some((
1124 prefix,
1125 AgentScope {
1126 capabilities,
1127 expires_at: (expires != 0).then_some(expires),
1128 },
1129 ))
1130}
1131
1132pub fn serialize_source_seal_couples(couples: &[SourceSeal]) -> Result<Vec<u8>, AttachmentError> {
1146 use cesride::{Counter, Matter, Saider, Seqner, counter};
1147
1148 let count = u32::try_from(couples.len())
1149 .map_err(|_| AttachmentError::Encode("too many source seal couples".into()))?;
1150 let mut out = Counter::new_with_code_and_count(counter::Codex::SealSourceCouples, count)
1151 .and_then(|c| c.qb64())
1152 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1153
1154 for couple in couples {
1155 let seqner = Seqner::new_with_sn(couple.s.value())
1156 .and_then(|s| s.qb64())
1157 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1158 let saider = Saider::new_with_qb64(couple.d.as_str())
1159 .and_then(|s| s.qb64())
1160 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1161 out.push_str(&seqner);
1162 out.push_str(&saider);
1163 }
1164
1165 Ok(out.into_bytes())
1166}
1167
1168pub fn parse_source_seal_couples(bytes: &[u8]) -> Result<Vec<SourceSeal>, AttachmentError> {
1176 let s = std::str::from_utf8(bytes)
1177 .map_err(|e| AttachmentError::Decode(format!("non-utf8 source-seal group: {e}")))?;
1178 let (couples, rest) = parse_source_seal_group(s)?;
1179 if !rest.is_empty() {
1180 return Err(AttachmentError::Decode(format!(
1181 "trailing bytes after source-seal group: {rest:?}"
1182 )));
1183 }
1184 Ok(couples)
1185}
1186
1187fn parse_source_seal_group(s: &str) -> Result<(Vec<SourceSeal>, &str), AttachmentError> {
1190 use cesride::{Matter, Saider, Seqner};
1191
1192 let rest = s.strip_prefix("-G").ok_or_else(|| {
1193 AttachmentError::Decode("source-seal group must start with -G counter code".into())
1194 })?;
1195 if rest.len() < 2 {
1196 return Err(AttachmentError::Decode(
1197 "truncated -G counter header".into(),
1198 ));
1199 }
1200 let (count_b64, mut cursor) = rest.split_at(2);
1201 let count = decode_count_b64(count_b64)?;
1202
1203 let mut out = Vec::with_capacity(count);
1204 for _ in 0..count {
1205 if cursor.len() < SEQNER_QB64_LEN {
1207 return Err(AttachmentError::Decode(
1208 "truncated Seqner in -G couple".into(),
1209 ));
1210 }
1211 let (seqner_qb64, after_seqner) = cursor.split_at(SEQNER_QB64_LEN);
1212 let seqner = Seqner::new_with_qb64(seqner_qb64)
1213 .map_err(|e| AttachmentError::Decode(format!("Seqner: {e}")))?;
1214 let sn = seqner
1215 .sn()
1216 .map_err(|e| AttachmentError::Decode(format!("Seqner sn: {e}")))?;
1217
1218 let first = *after_seqner.as_bytes().first().ok_or_else(|| {
1220 AttachmentError::Decode("truncated -G couple: expected a Saider".into())
1221 })?;
1222 let fs = saider_qb64_len(first).ok_or_else(|| {
1223 AttachmentError::Decode(format!("unsupported Saider code byte {first:?}"))
1224 })?;
1225 if after_seqner.len() < fs {
1226 return Err(AttachmentError::Decode(
1227 "truncated Saider in -G couple".into(),
1228 ));
1229 }
1230 let (saider_qb64, remainder) = after_seqner.split_at(fs);
1231 let saider = Saider::new_with_qb64(saider_qb64)
1232 .and_then(|s| s.qb64())
1233 .map_err(|e| AttachmentError::Decode(format!("Saider: {e}")))?;
1234 cursor = remainder;
1235
1236 out.push(SourceSeal {
1237 s: KeriSequence::new(sn),
1238 d: Said::new_unchecked(saider),
1239 });
1240 }
1241 Ok((out, cursor))
1242}
1243
1244pub fn parse_delegated_attachment(
1253 bytes: &[u8],
1254) -> Result<(Vec<IndexedSignature>, Vec<SourceSeal>), AttachmentError> {
1255 let s = std::str::from_utf8(bytes)
1256 .map_err(|e| AttachmentError::Decode(format!("non-utf8 attachment: {e}")))?;
1257 if s.is_empty() {
1258 return Ok((vec![], vec![]));
1259 }
1260 let (sigs, rest) = parse_sig_group(s)?;
1261 if rest.is_empty() {
1262 return Ok((sigs, vec![]));
1263 }
1264 let (couples, tail) = parse_source_seal_group(rest)?;
1265 if !tail.is_empty() {
1266 return Err(AttachmentError::Decode(format!(
1267 "trailing bytes after delegated attachment: {tail:?}"
1268 )));
1269 }
1270 Ok((sigs, couples))
1271}
1272
1273const SEQNER_QB64_LEN: usize = 24;
1275
1276fn saider_qb64_len(first: u8) -> Option<usize> {
1281 matches!(first, b'E' | b'F' | b'G' | b'H' | b'I').then_some(44)
1282}
1283
1284#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1298pub struct SignedEvent {
1299 pub event: Event,
1301 pub signatures: Vec<IndexedSignature>,
1303}
1304
1305impl SignedEvent {
1306 pub fn new(event: Event, signatures: Vec<IndexedSignature>) -> Self {
1308 Self { event, signatures }
1309 }
1310
1311 pub fn said(&self) -> &Said {
1313 self.event.said()
1314 }
1315
1316 pub fn sequence(&self) -> KeriSequence {
1318 self.event.sequence()
1319 }
1320
1321 pub fn prefix(&self) -> &Prefix {
1323 self.event.prefix()
1324 }
1325}
1326
1327pub fn serialize_attachment(signatures: &[IndexedSignature]) -> Result<Vec<u8>, AttachmentError> {
1333 use cesride::{Indexer, Siger, indexer};
1334
1335 let mut out = String::new();
1336 out.push_str("-A");
1337 out.push_str(&encode_count_b64(signatures.len())?);
1338
1339 for sig in signatures {
1340 let (code, ondex) = if sig.prior_index.is_some_and(|j| j != sig.index) {
1345 (indexer::Codex::Ed25519_Big, sig.prior_index)
1346 } else {
1347 (indexer::Codex::Ed25519, None)
1348 };
1349 let siger = Siger::new(
1350 None,
1351 Some(sig.index),
1352 ondex,
1353 Some(code),
1354 Some(&sig.sig),
1355 None,
1356 None,
1357 None,
1358 )
1359 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1360 let qb64 = siger
1361 .qb64()
1362 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1363 out.push_str(&qb64);
1364 }
1365
1366 Ok(out.into_bytes())
1367}
1368
1369fn indexer_hard_len(first: u8) -> usize {
1373 if first.is_ascii_digit() { 2 } else { 1 }
1374}
1375
1376fn indexer_sizage(code: &str) -> Option<(usize, usize)> {
1382 Some(match code {
1383 "A" | "B" | "C" | "D" | "E" | "F" => (88, 0),
1385 "2A" | "2B" | "2C" | "2D" | "2E" | "2F" => (92, 2),
1387 "0A" | "0B" => (156, 1),
1389 "3A" | "3B" => (160, 3),
1390 _ => return None,
1391 })
1392}
1393
1394pub fn parse_attachment(bytes: &[u8]) -> Result<Vec<IndexedSignature>, AttachmentError> {
1401 let s = std::str::from_utf8(bytes)
1402 .map_err(|e| AttachmentError::Decode(format!("non-utf8 attachment: {e}")))?;
1403
1404 if s.is_empty() {
1405 return Ok(vec![]);
1406 }
1407
1408 let (sigs, rest) = parse_sig_group(s)?;
1409 if !rest.is_empty() {
1410 return Err(AttachmentError::Decode(format!(
1411 "trailing bytes after signature group: {rest:?}"
1412 )));
1413 }
1414 Ok(sigs)
1415}
1416
1417fn parse_sig_group(s: &str) -> Result<(Vec<IndexedSignature>, &str), AttachmentError> {
1421 use cesride::{Indexer, Siger};
1422
1423 let rest = s.strip_prefix("-A").ok_or_else(|| {
1424 AttachmentError::Decode("attachment must start with -A counter code".into())
1425 })?;
1426 if rest.len() < 2 {
1427 return Err(AttachmentError::Decode("truncated counter header".into()));
1428 }
1429 let (count_b64, body) = rest.split_at(2);
1430 let count = decode_count_b64(count_b64)?;
1431
1432 let mut out = Vec::with_capacity(count);
1433 let mut cursor = body;
1434 for _ in 0..count {
1435 let first = *cursor.as_bytes().first().ok_or_else(|| {
1436 AttachmentError::Decode("truncated group: expected another signature".into())
1437 })?;
1438 let hs = indexer_hard_len(first);
1439 if cursor.len() < hs {
1440 return Err(AttachmentError::Decode("truncated siger hard code".into()));
1441 }
1442 let code = &cursor[..hs];
1443 let (fs, os) = indexer_sizage(code)
1444 .ok_or_else(|| AttachmentError::Decode(format!("unknown indexer code {code:?}")))?;
1445 if cursor.len() < fs {
1446 return Err(AttachmentError::Decode(format!(
1447 "insufficient bytes for {code} siger: need {fs}, have {}",
1448 cursor.len()
1449 )));
1450 }
1451 let (siger_qb64, remainder) = cursor.split_at(fs);
1452 cursor = remainder;
1453
1454 let siger = Siger::new(None, None, None, None, None, None, Some(siger_qb64), None)
1455 .map_err(|e| AttachmentError::Decode(format!("Siger: {e}")))?;
1456 let prior_index = (os > 0).then(|| siger.ondex());
1459
1460 out.push(IndexedSignature {
1461 index: siger.index(),
1462 prior_index,
1463 sig: siger.raw(),
1464 });
1465 }
1466
1467 Ok((out, cursor))
1468}
1469
1470#[derive(Debug, thiserror::Error)]
1472#[non_exhaustive]
1473pub enum AttachmentError {
1474 #[error("attachment encode: {0}")]
1476 Encode(String),
1477 #[error("attachment decode: {0}")]
1479 Decode(String),
1480}
1481
1482const B64_ALPHA: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1484
1485fn encode_count_b64(count: usize) -> Result<String, AttachmentError> {
1486 if count >= 64 * 64 {
1487 return Err(AttachmentError::Encode(format!(
1488 "count {count} exceeds 2-char base64url max (4095)"
1489 )));
1490 }
1491 let hi = B64_ALPHA[(count >> 6) & 0x3f] as char;
1492 let lo = B64_ALPHA[count & 0x3f] as char;
1493 Ok(format!("{hi}{lo}"))
1494}
1495
1496fn decode_count_b64(s: &str) -> Result<usize, AttachmentError> {
1497 let mut it = s.chars();
1498 let hi = it
1499 .next()
1500 .and_then(b64_index)
1501 .ok_or_else(|| AttachmentError::Decode(format!("invalid count hi char: {s:?}")))?;
1502 let lo = it
1503 .next()
1504 .and_then(b64_index)
1505 .ok_or_else(|| AttachmentError::Decode(format!("invalid count lo char: {s:?}")))?;
1506 Ok((hi << 6) | lo)
1507}
1508
1509fn b64_index(c: char) -> Option<usize> {
1510 B64_ALPHA.iter().position(|&b| b as char == c)
1511}
1512
1513#[cfg(test)]
1514#[allow(clippy::unwrap_used)]
1515mod tests {
1516 use super::*;
1517
1518 #[test]
1519 fn attachment_roundtrip_single_sig() {
1520 let sigs = vec![IndexedSignature {
1521 index: 0,
1522 prior_index: None,
1523 sig: vec![0x42u8; 64],
1524 }];
1525 let bytes = serialize_attachment(&sigs).unwrap();
1526 let s = std::str::from_utf8(&bytes).unwrap();
1527 assert!(s.starts_with("-AAB"), "expected -AAB prefix, got {s:?}");
1528 let back = parse_attachment(&bytes).unwrap();
1529 assert_eq!(back.len(), 1);
1530 assert_eq!(back[0].index, 0);
1531 assert_eq!(back[0].sig, vec![0x42u8; 64]);
1532 }
1533
1534 #[test]
1535 fn attachment_roundtrip_three_sigs() {
1536 let sigs = vec![
1537 IndexedSignature {
1538 index: 0,
1539 prior_index: None,
1540 sig: vec![0x01u8; 64],
1541 },
1542 IndexedSignature {
1543 index: 1,
1544 prior_index: None,
1545 sig: vec![0x02u8; 64],
1546 },
1547 IndexedSignature {
1548 index: 2,
1549 prior_index: None,
1550 sig: vec![0x03u8; 64],
1551 },
1552 ];
1553 let bytes = serialize_attachment(&sigs).unwrap();
1554 let s = std::str::from_utf8(&bytes).unwrap();
1555 assert!(s.starts_with("-AAD"), "expected -AAD prefix, got {s:?}");
1556 let back = parse_attachment(&bytes).unwrap();
1557 assert_eq!(back.len(), 3);
1558 for (i, sig) in back.iter().enumerate() {
1559 assert_eq!(sig.index, i as u32);
1560 }
1561 }
1562
1563 #[test]
1564 fn attachment_empty() {
1565 let bytes = serialize_attachment(&[]).unwrap();
1566 assert_eq!(bytes, b"-AAA");
1567 let back = parse_attachment(&bytes).unwrap();
1568 assert!(back.is_empty());
1569 }
1570
1571 #[test]
1572 fn keri_sequence_serializes_as_hex() {
1573 assert_eq!(
1574 serde_json::to_string(&KeriSequence::new(0)).unwrap(),
1575 "\"0\""
1576 );
1577 assert_eq!(
1578 serde_json::to_string(&KeriSequence::new(10)).unwrap(),
1579 "\"a\""
1580 );
1581 assert_eq!(
1582 serde_json::to_string(&KeriSequence::new(255)).unwrap(),
1583 "\"ff\""
1584 );
1585 }
1586
1587 #[test]
1588 fn keri_sequence_deserializes_from_hex() {
1589 let s: KeriSequence = serde_json::from_str("\"0\"").unwrap();
1590 assert_eq!(s.value(), 0);
1591 let s: KeriSequence = serde_json::from_str("\"a\"").unwrap();
1592 assert_eq!(s.value(), 10);
1593 let s: KeriSequence = serde_json::from_str("\"ff\"").unwrap();
1594 assert_eq!(s.value(), 255);
1595 }
1596
1597 #[test]
1598 fn keri_sequence_rejects_invalid_hex() {
1599 assert!(serde_json::from_str::<KeriSequence>("\"not_hex\"").is_err());
1600 }
1601
1602 #[test]
1603 fn icp_event_always_serializes_d_a_c() {
1604 use crate::types::{CesrKey, Threshold, VersionString};
1605 let icp = IcpEvent {
1606 v: VersionString::placeholder(),
1607 d: Said::default(),
1608 i: Prefix::new_unchecked("ETest123".to_string()),
1609 s: KeriSequence::new(0),
1610 kt: Threshold::Simple(1),
1611 k: vec![CesrKey::new_unchecked("DKey123".to_string())],
1612 nt: Threshold::Simple(1),
1613 n: vec![Said::new_unchecked("ENext456".to_string())],
1614 bt: Threshold::Simple(0),
1615 b: vec![],
1616 c: vec![],
1617 a: vec![],
1618 };
1619 let json = serde_json::to_string(&icp).unwrap();
1620 assert!(json.contains("\"d\":"), "d must always be present");
1622 assert!(json.contains("\"a\":"), "a must always be present");
1623 assert!(json.contains("\"c\":"), "c must always be present");
1624 assert!(!json.contains("\"x\":"), "empty x must be omitted");
1626 assert!(json.contains("\"s\":\"0\""), "s must serialize as hex");
1627 }
1628
1629 #[test]
1630 fn event_enum_deserializes_by_t_field() {
1631 let json = r#"{"v":"KERI10JSON000000_","t":"icp","d":"ETestSaid","i":"E123","s":"0","kt":"1","k":["DKey"],"nt":"1","n":["ENext"],"bt":"0","b":[]}"#;
1632 let event: Event = serde_json::from_str(json).unwrap();
1633 assert!(event.is_inception());
1634 assert_eq!(event.sequence().value(), 0);
1635 }
1636
1637 #[test]
1638 fn digest_seal_roundtrips() {
1639 let seal = Seal::digest("EDigest123");
1640 let json = serde_json::to_string(&seal).unwrap();
1641 assert_eq!(json, r#"{"d":"EDigest123"}"#);
1642 let parsed: Seal = serde_json::from_str(&json).unwrap();
1643 assert_eq!(seal, parsed);
1644 }
1645
1646 #[test]
1647 fn seal_event_location_roundtrips() {
1648 let seal = Seal::EventLocation {
1650 i: Prefix::new_unchecked("EPrefix".to_string()),
1651 s: KeriSequence::new(3),
1652 p: Said::new_unchecked("EPrior".to_string()),
1653 t: "ixn".to_string(),
1654 d: Said::new_unchecked("EDigest".to_string()),
1655 };
1656 let json = serde_json::to_string(&seal).unwrap();
1657 let parsed: Seal = serde_json::from_str(&json).unwrap();
1658 assert_eq!(seal, parsed);
1659 assert_eq!(parsed.digest_value().map(|d| d.as_str()), Some("EDigest"));
1660 }
1661
1662 #[test]
1663 fn seal_rejects_malformed_i_s() {
1664 let r: Result<Seal, _> = serde_json::from_str(r#"{"i":"EPrefix","s":"3"}"#);
1667 assert!(r.is_err(), "malformed {{i,s}} seal must be rejected");
1668 let r2: Result<Seal, _> = serde_json::from_str(r#"{"zz":"x"}"#);
1670 assert!(r2.is_err());
1671 }
1672
1673 #[test]
1674 fn key_event_seal_roundtrips() {
1675 let seal = Seal::key_event(
1676 Prefix::new_unchecked("EPrefix".to_string()),
1677 KeriSequence::new(1),
1678 Said::new_unchecked("ESaid".to_string()),
1679 );
1680 let json = serde_json::to_string(&seal).unwrap();
1681 let parsed: Seal = serde_json::from_str(&json).unwrap();
1682 assert_eq!(seal, parsed);
1683 }
1684
1685 #[test]
1686 fn indexed_signature_serde_roundtrip() {
1687 let sig = IndexedSignature {
1688 index: 2,
1689 prior_index: None,
1690 sig: vec![0xab; 64],
1691 };
1692 let json = serde_json::to_string(&sig).unwrap();
1693 assert!(json.contains("\"index\":2"));
1694 assert!(
1695 !json.contains("prior_index"),
1696 "a None prior_index must be omitted from the wire: {json}"
1697 );
1698 let parsed: IndexedSignature = serde_json::from_str(&json).unwrap();
1699 assert_eq!(parsed, sig);
1700 }
1701
1702 #[test]
1703 fn signed_event_accessors() {
1704 use crate::types::{CesrKey, Threshold, VersionString};
1705 let icp = IcpEvent {
1706 v: VersionString::placeholder(),
1707 d: Said::new_unchecked("ESAID123".to_string()),
1708 i: Prefix::new_unchecked("EPrefix".to_string()),
1709 s: KeriSequence::new(0),
1710 kt: Threshold::Simple(1),
1711 k: vec![CesrKey::new_unchecked("DKey".to_string())],
1712 nt: Threshold::Simple(1),
1713 n: vec![Said::new_unchecked("ENext".to_string())],
1714 bt: Threshold::Simple(0),
1715 b: vec![],
1716 c: vec![],
1717 a: vec![],
1718 };
1719 let signed = SignedEvent::new(
1720 Event::Icp(icp),
1721 vec![IndexedSignature {
1722 index: 0,
1723 prior_index: None,
1724 sig: vec![0u8; 64],
1725 }],
1726 );
1727 assert_eq!(signed.said().as_str(), "ESAID123");
1728 assert_eq!(signed.sequence().value(), 0);
1729 assert_eq!(signed.prefix().as_str(), "EPrefix");
1730 assert_eq!(signed.signatures.len(), 1);
1731 assert_eq!(signed.signatures[0].index, 0);
1732 }
1733
1734 #[test]
1735 fn seal_digest_value() {
1736 let seal = Seal::digest("ETest123");
1737 assert_eq!(seal.digest_value().unwrap().as_str(), "ETest123");
1738 let latest = Seal::LatestEstablishment {
1739 i: Prefix::new_unchecked("EPrefix".to_string()),
1740 };
1741 assert!(latest.digest_value().is_none());
1742 }
1743}