1use serde::ser::SerializeMap;
7use serde::{Deserialize, Serialize, Serializer};
8use std::fmt;
9
10use crate::types::{CesrKey, ConfigTrait, Prefix, Said, Threshold, VersionString};
11
12pub const KERI_VERSION_PREFIX: &str = "KERI10JSON";
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct KeriSequence(u128);
29
30#[cfg(feature = "schema")]
31impl schemars::JsonSchema for KeriSequence {
32 fn schema_name() -> String {
33 "KeriSequence".to_string()
34 }
35
36 fn json_schema(_gen: &mut schemars::r#gen::SchemaGenerator) -> schemars::schema::Schema {
37 schemars::schema::SchemaObject {
38 instance_type: Some(schemars::schema::InstanceType::String.into()),
39 ..Default::default()
40 }
41 .into()
42 }
43}
44
45impl KeriSequence {
46 pub fn new(value: u128) -> Self {
48 Self(value)
49 }
50
51 pub fn value(self) -> u128 {
53 self.0
54 }
55}
56
57impl fmt::Display for KeriSequence {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 write!(f, "{:x}", self.0)
60 }
61}
62
63impl Serialize for KeriSequence {
64 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
65 serializer.serialize_str(&format!("{:x}", self.0))
66 }
67}
68
69impl<'de> Deserialize<'de> for KeriSequence {
70 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
71 let s = String::deserialize(deserializer)?;
72 let value = u128::from_str_radix(&s, 16)
73 .map_err(|_| serde::de::Error::custom(format!("invalid hex sequence: {s:?}")))?;
74 Ok(KeriSequence(value))
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
92#[non_exhaustive]
93pub enum Seal {
94 Digest {
96 d: Said,
98 },
99 SourceEvent {
101 s: KeriSequence,
103 d: Said,
105 },
106 KeyEvent {
108 i: Prefix,
110 s: KeriSequence,
112 d: Said,
114 },
115 EventLocation {
117 i: Prefix,
119 s: KeriSequence,
121 p: Said,
123 t: String,
125 d: Said,
127 },
128 LatestEstablishment {
130 i: Prefix,
132 },
133 #[cfg(feature = "seal-extensions")]
135 MerkleRoot {
136 rd: Said,
138 },
139 #[cfg(feature = "seal-extensions")]
141 RegistrarBacker {
142 bi: Prefix,
144 d: Said,
146 },
147}
148
149impl Seal {
150 pub fn digest(said: impl Into<String>) -> Self {
152 Self::Digest {
153 d: Said::new_unchecked(said.into()),
154 }
155 }
156
157 pub fn key_event(prefix: Prefix, sequence: KeriSequence, said: Said) -> Self {
159 Self::KeyEvent {
160 i: prefix,
161 s: sequence,
162 d: said,
163 }
164 }
165
166 pub fn digest_value(&self) -> Option<&Said> {
168 match self {
169 Seal::Digest { d } => Some(d),
170 Seal::SourceEvent { d, .. } => Some(d),
171 Seal::KeyEvent { d, .. } => Some(d),
172 Seal::EventLocation { d, .. } => Some(d),
173 Seal::LatestEstablishment { .. } => None,
174 #[cfg(feature = "seal-extensions")]
175 Seal::RegistrarBacker { d, .. } => Some(d),
176 #[cfg(feature = "seal-extensions")]
177 Seal::MerkleRoot { rd } => Some(rd),
178 }
179 }
180}
181
182impl Serialize for Seal {
183 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
184 match self {
185 Seal::Digest { d } => {
186 let mut map = serializer.serialize_map(Some(1))?;
187 map.serialize_entry("d", d)?;
188 map.end()
189 }
190 Seal::SourceEvent { s, d } => {
191 let mut map = serializer.serialize_map(Some(2))?;
192 map.serialize_entry("s", s)?;
193 map.serialize_entry("d", d)?;
194 map.end()
195 }
196 Seal::KeyEvent { i, s, d } => {
197 let mut map = serializer.serialize_map(Some(3))?;
198 map.serialize_entry("i", i)?;
199 map.serialize_entry("s", s)?;
200 map.serialize_entry("d", d)?;
201 map.end()
202 }
203 Seal::EventLocation { i, s, p, t, d } => {
204 let mut map = serializer.serialize_map(Some(5))?;
205 map.serialize_entry("i", i)?;
206 map.serialize_entry("s", s)?;
207 map.serialize_entry("p", p)?;
208 map.serialize_entry("t", t)?;
209 map.serialize_entry("d", d)?;
210 map.end()
211 }
212 Seal::LatestEstablishment { i } => {
213 let mut map = serializer.serialize_map(Some(1))?;
214 map.serialize_entry("i", i)?;
215 map.end()
216 }
217 #[cfg(feature = "seal-extensions")]
218 Seal::MerkleRoot { rd } => {
219 let mut map = serializer.serialize_map(Some(1))?;
220 map.serialize_entry("rd", rd)?;
221 map.end()
222 }
223 #[cfg(feature = "seal-extensions")]
224 Seal::RegistrarBacker { bi, d } => {
225 let mut map = serializer.serialize_map(Some(2))?;
226 map.serialize_entry("bi", bi)?;
227 map.serialize_entry("d", d)?;
228 map.end()
229 }
230 }
231 }
232}
233
234impl<'de> Deserialize<'de> for Seal {
235 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
236 let map: serde_json::Map<String, serde_json::Value> =
237 serde_json::Map::deserialize(deserializer)?;
238
239 let want_str = |k: &str| -> Result<String, D::Error> {
240 map.get(k)
241 .and_then(|v| v.as_str())
242 .map(|v| v.to_string())
243 .ok_or_else(|| {
244 serde::de::Error::custom(format!("seal field `{k}` must be a string"))
245 })
246 };
247 let want_seq =
248 |k: &str| -> Result<KeriSequence, D::Error> {
249 serde_json::from_value(map.get(k).cloned().ok_or_else(|| {
250 serde::de::Error::custom(format!("seal field `{k}` required"))
251 })?)
252 .map_err(serde::de::Error::custom)
253 };
254
255 let mut keys: Vec<&str> = map.keys().map(String::as_str).collect();
257 keys.sort_unstable();
258
259 match keys.as_slice() {
260 ["d"] => Ok(Seal::Digest {
261 d: Said::new_unchecked(want_str("d")?),
262 }),
263 ["d", "s"] => Ok(Seal::SourceEvent {
264 s: want_seq("s")?,
265 d: Said::new_unchecked(want_str("d")?),
266 }),
267 ["d", "i", "s"] => Ok(Seal::KeyEvent {
268 i: Prefix::new_unchecked(want_str("i")?),
269 s: want_seq("s")?,
270 d: Said::new_unchecked(want_str("d")?),
271 }),
272 ["d", "i", "p", "s", "t"] => Ok(Seal::EventLocation {
273 i: Prefix::new_unchecked(want_str("i")?),
274 s: want_seq("s")?,
275 p: Said::new_unchecked(want_str("p")?),
276 t: want_str("t")?,
277 d: Said::new_unchecked(want_str("d")?),
278 }),
279 ["i"] => Ok(Seal::LatestEstablishment {
280 i: Prefix::new_unchecked(want_str("i")?),
281 }),
282 #[cfg(feature = "seal-extensions")]
283 ["rd"] => Ok(Seal::MerkleRoot {
284 rd: Said::new_unchecked(want_str("rd")?),
285 }),
286 #[cfg(feature = "seal-extensions")]
287 ["bi", "d"] => Ok(Seal::RegistrarBacker {
288 bi: Prefix::new_unchecked(want_str("bi")?),
289 d: Said::new_unchecked(want_str("d")?),
290 }),
291 other => Err(serde::de::Error::custom(format!(
292 "unrecognized seal field set: {other:?}"
293 ))),
294 }
295 }
296}
297
298#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
307#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
308pub struct IcpEvent {
309 pub v: VersionString,
311 pub d: Said,
313 pub i: Prefix,
315 pub s: KeriSequence,
317 pub kt: Threshold,
319 pub k: Vec<CesrKey>,
321 pub nt: Threshold,
323 pub n: Vec<Said>,
325 pub bt: Threshold,
327 #[serde(default)]
329 pub b: Vec<Prefix>,
330 #[serde(default)]
332 pub c: Vec<ConfigTrait>,
333 #[serde(default)]
335 pub a: Vec<Seal>,
336}
337
338pub struct IcpEventInit {
344 pub v: VersionString,
346 pub d: Said,
348 pub i: Prefix,
350 pub s: KeriSequence,
352 pub kt: Threshold,
354 pub k: Vec<CesrKey>,
356 pub nt: Threshold,
358 pub n: Vec<Said>,
360 pub bt: Threshold,
362 pub b: Vec<Prefix>,
364 pub c: Vec<ConfigTrait>,
366 pub a: Vec<Seal>,
368}
369
370impl IcpEvent {
371 pub fn new(init: IcpEventInit) -> Self {
373 Self {
374 v: init.v,
375 d: init.d,
376 i: init.i,
377 s: init.s,
378 kt: init.kt,
379 k: init.k,
380 nt: init.nt,
381 n: init.n,
382 bt: init.bt,
383 b: init.b,
384 c: init.c,
385 a: init.a,
386 }
387 }
388}
389
390impl Serialize for IcpEvent {
392 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
393 let field_count = 13;
394 let mut map = serializer.serialize_map(Some(field_count))?;
395 map.serialize_entry("v", &self.v)?;
396 map.serialize_entry("t", "icp")?;
397 map.serialize_entry("d", &self.d)?;
398 map.serialize_entry("i", &self.i)?;
399 map.serialize_entry("s", &self.s)?;
400 map.serialize_entry("kt", &self.kt)?;
401 map.serialize_entry("k", &self.k)?;
402 map.serialize_entry("nt", &self.nt)?;
403 map.serialize_entry("n", &self.n)?;
404 map.serialize_entry("bt", &self.bt)?;
405 map.serialize_entry("b", &self.b)?;
406 map.serialize_entry("c", &self.c)?;
407 map.serialize_entry("a", &self.a)?;
408 map.end()
409 }
410}
411
412#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
416#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
417pub struct RotEvent {
418 pub v: VersionString,
420 pub d: Said,
422 pub i: Prefix,
424 pub s: KeriSequence,
426 pub p: Said,
428 pub kt: Threshold,
430 pub k: Vec<CesrKey>,
432 pub nt: Threshold,
434 pub n: Vec<Said>,
436 pub bt: Threshold,
438 #[serde(default)]
440 pub br: Vec<Prefix>,
441 #[serde(default)]
443 pub ba: Vec<Prefix>,
444 #[serde(default)]
446 pub c: Vec<ConfigTrait>,
447 #[serde(default)]
449 pub a: Vec<Seal>,
450}
451
452pub struct RotEventInit {
455 pub v: VersionString,
457 pub d: Said,
459 pub i: Prefix,
461 pub s: KeriSequence,
463 pub p: Said,
465 pub kt: Threshold,
467 pub k: Vec<CesrKey>,
469 pub nt: Threshold,
471 pub n: Vec<Said>,
473 pub bt: Threshold,
475 pub br: Vec<Prefix>,
477 pub ba: Vec<Prefix>,
479 pub c: Vec<ConfigTrait>,
481 pub a: Vec<Seal>,
483}
484
485impl RotEvent {
486 pub fn new(init: RotEventInit) -> Self {
488 Self {
489 v: init.v,
490 d: init.d,
491 i: init.i,
492 s: init.s,
493 p: init.p,
494 kt: init.kt,
495 k: init.k,
496 nt: init.nt,
497 n: init.n,
498 bt: init.bt,
499 br: init.br,
500 ba: init.ba,
501 c: init.c,
502 a: init.a,
503 }
504 }
505}
506
507impl Serialize for RotEvent {
509 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
510 let field_count = 14;
511 let mut map = serializer.serialize_map(Some(field_count))?;
512 map.serialize_entry("v", &self.v)?;
513 map.serialize_entry("t", "rot")?;
514 map.serialize_entry("d", &self.d)?;
515 map.serialize_entry("i", &self.i)?;
516 map.serialize_entry("s", &self.s)?;
517 map.serialize_entry("p", &self.p)?;
518 map.serialize_entry("kt", &self.kt)?;
519 map.serialize_entry("k", &self.k)?;
520 map.serialize_entry("nt", &self.nt)?;
521 map.serialize_entry("n", &self.n)?;
522 map.serialize_entry("bt", &self.bt)?;
523 map.serialize_entry("br", &self.br)?;
524 map.serialize_entry("ba", &self.ba)?;
525 map.serialize_entry("a", &self.a)?;
526 map.end()
527 }
528}
529
530#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
534#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
535pub struct IxnEvent {
536 pub v: VersionString,
538 pub d: Said,
540 pub i: Prefix,
542 pub s: KeriSequence,
544 pub p: Said,
546 pub a: Vec<Seal>,
548}
549
550pub struct IxnEventInit {
552 pub v: VersionString,
553 pub d: Said,
554 pub i: Prefix,
555 pub s: KeriSequence,
556 pub p: Said,
557 pub a: Vec<Seal>,
558}
559
560impl IxnEvent {
561 pub fn new(init: IxnEventInit) -> Self {
563 Self {
564 v: init.v,
565 d: init.d,
566 i: init.i,
567 s: init.s,
568 p: init.p,
569 a: init.a,
570 }
571 }
572}
573
574impl Serialize for IxnEvent {
576 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
577 let field_count = 7;
578 let mut map = serializer.serialize_map(Some(field_count))?;
579 map.serialize_entry("v", &self.v)?;
580 map.serialize_entry("t", "ixn")?;
581 map.serialize_entry("d", &self.d)?;
582 map.serialize_entry("i", &self.i)?;
583 map.serialize_entry("s", &self.s)?;
584 map.serialize_entry("p", &self.p)?;
585 map.serialize_entry("a", &self.a)?;
586 map.end()
587 }
588}
589
590#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
608#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
609pub struct DipEvent {
610 pub v: VersionString,
612 pub d: Said,
614 pub i: Prefix,
616 pub s: KeriSequence,
618 pub kt: Threshold,
620 pub k: Vec<CesrKey>,
622 pub nt: Threshold,
624 pub n: Vec<Said>,
626 pub bt: Threshold,
628 #[serde(default)]
630 pub b: Vec<Prefix>,
631 #[serde(default)]
633 pub c: Vec<ConfigTrait>,
634 #[serde(default)]
636 pub a: Vec<Seal>,
637 pub di: Prefix,
639 #[serde(skip)]
645 pub source_seal: Option<SourceSeal>,
646}
647
648pub struct DipEventInit {
650 pub v: VersionString,
652 pub d: Said,
654 pub i: Prefix,
656 pub s: KeriSequence,
658 pub kt: Threshold,
660 pub k: Vec<CesrKey>,
662 pub nt: Threshold,
664 pub n: Vec<Said>,
666 pub bt: Threshold,
668 pub b: Vec<Prefix>,
670 pub c: Vec<ConfigTrait>,
672 pub a: Vec<Seal>,
674 pub di: Prefix,
676}
677
678impl DipEvent {
679 pub fn new(init: DipEventInit) -> Self {
681 Self {
682 v: init.v,
683 d: init.d,
684 i: init.i,
685 s: init.s,
686 kt: init.kt,
687 k: init.k,
688 nt: init.nt,
689 n: init.n,
690 bt: init.bt,
691 b: init.b,
692 c: init.c,
693 a: init.a,
694 di: init.di,
695 source_seal: None,
696 }
697 }
698}
699
700impl Serialize for DipEvent {
702 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
703 let field_count = 14;
704 let mut map = serializer.serialize_map(Some(field_count))?;
705 map.serialize_entry("v", &self.v)?;
706 map.serialize_entry("t", "dip")?;
707 map.serialize_entry("d", &self.d)?;
708 map.serialize_entry("i", &self.i)?;
709 map.serialize_entry("s", &self.s)?;
710 map.serialize_entry("kt", &self.kt)?;
711 map.serialize_entry("k", &self.k)?;
712 map.serialize_entry("nt", &self.nt)?;
713 map.serialize_entry("n", &self.n)?;
714 map.serialize_entry("bt", &self.bt)?;
715 map.serialize_entry("b", &self.b)?;
716 map.serialize_entry("c", &self.c)?;
717 map.serialize_entry("a", &self.a)?;
718 map.serialize_entry("di", &self.di)?;
719 map.end()
720 }
721}
722
723#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
729#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
730pub struct DrtEvent {
731 pub v: VersionString,
733 pub d: Said,
735 pub i: Prefix,
737 pub s: KeriSequence,
739 pub p: Said,
741 pub kt: Threshold,
743 pub k: Vec<CesrKey>,
745 pub nt: Threshold,
747 pub n: Vec<Said>,
749 pub bt: Threshold,
751 #[serde(default)]
753 pub br: Vec<Prefix>,
754 #[serde(default)]
756 pub ba: Vec<Prefix>,
757 #[serde(default)]
759 pub c: Vec<ConfigTrait>,
760 #[serde(default)]
762 pub a: Vec<Seal>,
763 pub di: Prefix,
765 #[serde(skip)]
769 pub source_seal: Option<SourceSeal>,
770}
771
772pub struct DrtEventInit {
774 pub v: VersionString,
776 pub d: Said,
778 pub i: Prefix,
780 pub s: KeriSequence,
782 pub p: Said,
784 pub kt: Threshold,
786 pub k: Vec<CesrKey>,
788 pub nt: Threshold,
790 pub n: Vec<Said>,
792 pub bt: Threshold,
794 pub br: Vec<Prefix>,
796 pub ba: Vec<Prefix>,
798 pub c: Vec<ConfigTrait>,
800 pub a: Vec<Seal>,
802 pub di: Prefix,
804}
805
806impl DrtEvent {
807 pub fn new(init: DrtEventInit) -> Self {
809 Self {
810 v: init.v,
811 d: init.d,
812 i: init.i,
813 s: init.s,
814 p: init.p,
815 kt: init.kt,
816 k: init.k,
817 nt: init.nt,
818 n: init.n,
819 bt: init.bt,
820 br: init.br,
821 ba: init.ba,
822 c: init.c,
823 a: init.a,
824 di: init.di,
825 source_seal: None,
826 }
827 }
828}
829
830impl Serialize for DrtEvent {
832 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
833 let field_count = 15;
834 let mut map = serializer.serialize_map(Some(field_count))?;
835 map.serialize_entry("v", &self.v)?;
836 map.serialize_entry("t", "drt")?;
837 map.serialize_entry("d", &self.d)?;
838 map.serialize_entry("i", &self.i)?;
839 map.serialize_entry("s", &self.s)?;
840 map.serialize_entry("p", &self.p)?;
841 map.serialize_entry("kt", &self.kt)?;
842 map.serialize_entry("k", &self.k)?;
843 map.serialize_entry("nt", &self.nt)?;
844 map.serialize_entry("n", &self.n)?;
845 map.serialize_entry("bt", &self.bt)?;
846 map.serialize_entry("br", &self.br)?;
847 map.serialize_entry("ba", &self.ba)?;
848 map.serialize_entry("a", &self.a)?;
849 map.serialize_entry("di", &self.di)?;
850 map.end()
851 }
852}
853
854#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
856#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
857#[serde(tag = "t")]
858pub enum Event {
859 #[serde(rename = "icp")]
861 Icp(IcpEvent),
862 #[serde(rename = "rot")]
864 Rot(RotEvent),
865 #[serde(rename = "ixn")]
867 Ixn(IxnEvent),
868 #[serde(rename = "dip")]
870 Dip(DipEvent),
871 #[serde(rename = "drt")]
873 Drt(DrtEvent),
874}
875
876impl Serialize for Event {
877 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
878 match self {
879 Event::Icp(e) => e.serialize(serializer),
880 Event::Rot(e) => e.serialize(serializer),
881 Event::Ixn(e) => e.serialize(serializer),
882 Event::Dip(e) => e.serialize(serializer),
883 Event::Drt(e) => e.serialize(serializer),
884 }
885 }
886}
887
888impl Event {
889 pub fn said(&self) -> &Said {
891 match self {
892 Event::Icp(e) => &e.d,
893 Event::Rot(e) => &e.d,
894 Event::Ixn(e) => &e.d,
895 Event::Dip(e) => &e.d,
896 Event::Drt(e) => &e.d,
897 }
898 }
899
900 pub fn sequence(&self) -> KeriSequence {
902 match self {
903 Event::Icp(e) => e.s,
904 Event::Rot(e) => e.s,
905 Event::Ixn(e) => e.s,
906 Event::Dip(e) => e.s,
907 Event::Drt(e) => e.s,
908 }
909 }
910
911 pub fn prefix(&self) -> &Prefix {
913 match self {
914 Event::Icp(e) => &e.i,
915 Event::Rot(e) => &e.i,
916 Event::Ixn(e) => &e.i,
917 Event::Dip(e) => &e.i,
918 Event::Drt(e) => &e.i,
919 }
920 }
921
922 pub fn previous(&self) -> Option<&Said> {
924 match self {
925 Event::Icp(_) | Event::Dip(_) => None,
926 Event::Rot(e) => Some(&e.p),
927 Event::Ixn(e) => Some(&e.p),
928 Event::Drt(e) => Some(&e.p),
929 }
930 }
931
932 pub fn keys(&self) -> Option<&[CesrKey]> {
934 match self {
935 Event::Icp(e) => Some(&e.k),
936 Event::Rot(e) => Some(&e.k),
937 Event::Dip(e) => Some(&e.k),
938 Event::Drt(e) => Some(&e.k),
939 Event::Ixn(_) => None,
940 }
941 }
942
943 pub fn next_commitments(&self) -> Option<&[Said]> {
945 match self {
946 Event::Icp(e) => Some(&e.n),
947 Event::Rot(e) => Some(&e.n),
948 Event::Dip(e) => Some(&e.n),
949 Event::Drt(e) => Some(&e.n),
950 Event::Ixn(_) => None,
951 }
952 }
953
954 pub fn anchors(&self) -> &[Seal] {
956 match self {
957 Event::Icp(e) => &e.a,
958 Event::Rot(e) => &e.a,
959 Event::Ixn(e) => &e.a,
960 Event::Dip(e) => &e.a,
961 Event::Drt(e) => &e.a,
962 }
963 }
964
965 pub fn delegator(&self) -> Option<&Prefix> {
967 match self {
968 Event::Dip(e) => Some(&e.di),
969 _ => None,
970 }
971 }
972
973 pub fn is_inception(&self) -> bool {
975 matches!(self, Event::Icp(_) | Event::Dip(_))
976 }
977
978 pub fn is_rotation(&self) -> bool {
980 matches!(self, Event::Rot(_) | Event::Drt(_))
981 }
982
983 pub fn is_interaction(&self) -> bool {
985 matches!(self, Event::Ixn(_))
986 }
987
988 pub fn is_delegated(&self) -> bool {
990 matches!(self, Event::Dip(_) | Event::Drt(_))
991 }
992
993 pub fn source_seal(&self) -> Option<&SourceSeal> {
996 match self {
997 Event::Dip(e) => e.source_seal.as_ref(),
998 Event::Drt(e) => e.source_seal.as_ref(),
999 _ => None,
1000 }
1001 }
1002}
1003
1004#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1019pub struct IndexedSignature {
1020 pub index: u32,
1022 #[serde(default, skip_serializing_if = "Option::is_none")]
1029 pub prior_index: Option<u32>,
1030 #[serde(with = "hex::serde")]
1032 pub sig: Vec<u8>,
1033}
1034
1035#[derive(Debug, Clone, PartialEq, Eq)]
1044#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1045pub struct SourceSeal {
1046 pub s: KeriSequence,
1048 pub d: Said,
1050}
1051
1052#[derive(Debug, Clone, PartialEq, Eq, Default)]
1061#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
1062pub struct AgentScope {
1063 pub capabilities: Vec<String>,
1066 pub expires_at: Option<i64>,
1068}
1069
1070const AGENT_SCOPE_MARKER: &str = "agentscope:";
1072
1073pub fn encode_agent_scope(agent_prefix: &str, scope: &AgentScope) -> String {
1086 let expires = scope.expires_at.unwrap_or(0);
1087 let caps = scope.capabilities.join(",");
1088 format!("{AGENT_SCOPE_MARKER}{agent_prefix}:{expires}:{caps}")
1089}
1090
1091pub fn decode_agent_scope(value: &str) -> Option<(String, AgentScope)> {
1097 let rest = value.strip_prefix(AGENT_SCOPE_MARKER)?;
1100 let mut parts = rest.splitn(3, ':');
1101 let prefix = parts.next()?.to_string();
1102 let expires: i64 = parts.next()?.parse().ok()?;
1103 let caps_csv = parts.next().unwrap_or("");
1104 let capabilities = if caps_csv.is_empty() {
1105 Vec::new()
1106 } else {
1107 caps_csv.split(',').map(|c| c.to_string()).collect()
1108 };
1109 Some((
1110 prefix,
1111 AgentScope {
1112 capabilities,
1113 expires_at: (expires != 0).then_some(expires),
1114 },
1115 ))
1116}
1117
1118pub fn serialize_source_seal_couples(couples: &[SourceSeal]) -> Result<Vec<u8>, AttachmentError> {
1132 use cesride::{Counter, Matter, Saider, Seqner, counter};
1133
1134 let count = u32::try_from(couples.len())
1135 .map_err(|_| AttachmentError::Encode("too many source seal couples".into()))?;
1136 let mut out = Counter::new_with_code_and_count(counter::Codex::SealSourceCouples, count)
1137 .and_then(|c| c.qb64())
1138 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1139
1140 for couple in couples {
1141 let seqner = Seqner::new_with_sn(couple.s.value())
1142 .and_then(|s| s.qb64())
1143 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1144 let saider = Saider::new_with_qb64(couple.d.as_str())
1145 .and_then(|s| s.qb64())
1146 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1147 out.push_str(&seqner);
1148 out.push_str(&saider);
1149 }
1150
1151 Ok(out.into_bytes())
1152}
1153
1154pub fn parse_source_seal_couples(bytes: &[u8]) -> Result<Vec<SourceSeal>, AttachmentError> {
1162 let s = std::str::from_utf8(bytes)
1163 .map_err(|e| AttachmentError::Decode(format!("non-utf8 source-seal group: {e}")))?;
1164 let (couples, rest) = parse_source_seal_group(s)?;
1165 if !rest.is_empty() {
1166 return Err(AttachmentError::Decode(format!(
1167 "trailing bytes after source-seal group: {rest:?}"
1168 )));
1169 }
1170 Ok(couples)
1171}
1172
1173fn parse_source_seal_group(s: &str) -> Result<(Vec<SourceSeal>, &str), AttachmentError> {
1176 use cesride::{Matter, Saider, Seqner};
1177
1178 let rest = s.strip_prefix("-G").ok_or_else(|| {
1179 AttachmentError::Decode("source-seal group must start with -G counter code".into())
1180 })?;
1181 if rest.len() < 2 {
1182 return Err(AttachmentError::Decode(
1183 "truncated -G counter header".into(),
1184 ));
1185 }
1186 let (count_b64, mut cursor) = rest.split_at(2);
1187 let count = decode_count_b64(count_b64)?;
1188
1189 let mut out = Vec::with_capacity(count);
1190 for _ in 0..count {
1191 if cursor.len() < SEQNER_QB64_LEN {
1193 return Err(AttachmentError::Decode(
1194 "truncated Seqner in -G couple".into(),
1195 ));
1196 }
1197 let (seqner_qb64, after_seqner) = cursor.split_at(SEQNER_QB64_LEN);
1198 let seqner = Seqner::new_with_qb64(seqner_qb64)
1199 .map_err(|e| AttachmentError::Decode(format!("Seqner: {e}")))?;
1200 let sn = seqner
1201 .sn()
1202 .map_err(|e| AttachmentError::Decode(format!("Seqner sn: {e}")))?;
1203
1204 let first = *after_seqner.as_bytes().first().ok_or_else(|| {
1206 AttachmentError::Decode("truncated -G couple: expected a Saider".into())
1207 })?;
1208 let fs = saider_qb64_len(first).ok_or_else(|| {
1209 AttachmentError::Decode(format!("unsupported Saider code byte {first:?}"))
1210 })?;
1211 if after_seqner.len() < fs {
1212 return Err(AttachmentError::Decode(
1213 "truncated Saider in -G couple".into(),
1214 ));
1215 }
1216 let (saider_qb64, remainder) = after_seqner.split_at(fs);
1217 let saider = Saider::new_with_qb64(saider_qb64)
1218 .and_then(|s| s.qb64())
1219 .map_err(|e| AttachmentError::Decode(format!("Saider: {e}")))?;
1220 cursor = remainder;
1221
1222 out.push(SourceSeal {
1223 s: KeriSequence::new(sn),
1224 d: Said::new_unchecked(saider),
1225 });
1226 }
1227 Ok((out, cursor))
1228}
1229
1230pub fn parse_delegated_attachment(
1239 bytes: &[u8],
1240) -> Result<(Vec<IndexedSignature>, Vec<SourceSeal>), AttachmentError> {
1241 let s = std::str::from_utf8(bytes)
1242 .map_err(|e| AttachmentError::Decode(format!("non-utf8 attachment: {e}")))?;
1243 if s.is_empty() {
1244 return Ok((vec![], vec![]));
1245 }
1246 let (sigs, rest) = parse_sig_group(s)?;
1247 if rest.is_empty() {
1248 return Ok((sigs, vec![]));
1249 }
1250 let (couples, tail) = parse_source_seal_group(rest)?;
1251 if !tail.is_empty() {
1252 return Err(AttachmentError::Decode(format!(
1253 "trailing bytes after delegated attachment: {tail:?}"
1254 )));
1255 }
1256 Ok((sigs, couples))
1257}
1258
1259const SEQNER_QB64_LEN: usize = 24;
1261
1262fn saider_qb64_len(first: u8) -> Option<usize> {
1267 matches!(first, b'E' | b'F' | b'G' | b'H' | b'I').then_some(44)
1268}
1269
1270#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1284pub struct SignedEvent {
1285 pub event: Event,
1287 pub signatures: Vec<IndexedSignature>,
1289}
1290
1291impl SignedEvent {
1292 pub fn new(event: Event, signatures: Vec<IndexedSignature>) -> Self {
1294 Self { event, signatures }
1295 }
1296
1297 pub fn said(&self) -> &Said {
1299 self.event.said()
1300 }
1301
1302 pub fn sequence(&self) -> KeriSequence {
1304 self.event.sequence()
1305 }
1306
1307 pub fn prefix(&self) -> &Prefix {
1309 self.event.prefix()
1310 }
1311}
1312
1313pub fn serialize_attachment(signatures: &[IndexedSignature]) -> Result<Vec<u8>, AttachmentError> {
1319 use cesride::{Indexer, Siger, indexer};
1320
1321 let mut out = String::new();
1322 out.push_str("-A");
1323 out.push_str(&encode_count_b64(signatures.len())?);
1324
1325 for sig in signatures {
1326 let (code, ondex) = if sig.prior_index.is_some_and(|j| j != sig.index) {
1331 (indexer::Codex::Ed25519_Big, sig.prior_index)
1332 } else {
1333 (indexer::Codex::Ed25519, None)
1334 };
1335 let siger = Siger::new(
1336 None,
1337 Some(sig.index),
1338 ondex,
1339 Some(code),
1340 Some(&sig.sig),
1341 None,
1342 None,
1343 None,
1344 )
1345 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1346 let qb64 = siger
1347 .qb64()
1348 .map_err(|e| AttachmentError::Encode(e.to_string()))?;
1349 out.push_str(&qb64);
1350 }
1351
1352 Ok(out.into_bytes())
1353}
1354
1355fn indexer_hard_len(first: u8) -> usize {
1359 if first.is_ascii_digit() { 2 } else { 1 }
1360}
1361
1362fn indexer_sizage(code: &str) -> Option<(usize, usize)> {
1368 Some(match code {
1369 "A" | "B" | "C" | "D" | "E" | "F" => (88, 0),
1371 "2A" | "2B" | "2C" | "2D" | "2E" | "2F" => (92, 2),
1373 "0A" | "0B" => (156, 1),
1375 "3A" | "3B" => (160, 3),
1376 _ => return None,
1377 })
1378}
1379
1380pub fn parse_attachment(bytes: &[u8]) -> Result<Vec<IndexedSignature>, AttachmentError> {
1387 let s = std::str::from_utf8(bytes)
1388 .map_err(|e| AttachmentError::Decode(format!("non-utf8 attachment: {e}")))?;
1389
1390 if s.is_empty() {
1391 return Ok(vec![]);
1392 }
1393
1394 let (sigs, rest) = parse_sig_group(s)?;
1395 if !rest.is_empty() {
1396 return Err(AttachmentError::Decode(format!(
1397 "trailing bytes after signature group: {rest:?}"
1398 )));
1399 }
1400 Ok(sigs)
1401}
1402
1403fn parse_sig_group(s: &str) -> Result<(Vec<IndexedSignature>, &str), AttachmentError> {
1407 use cesride::{Indexer, Siger};
1408
1409 let rest = s.strip_prefix("-A").ok_or_else(|| {
1410 AttachmentError::Decode("attachment must start with -A counter code".into())
1411 })?;
1412 if rest.len() < 2 {
1413 return Err(AttachmentError::Decode("truncated counter header".into()));
1414 }
1415 let (count_b64, body) = rest.split_at(2);
1416 let count = decode_count_b64(count_b64)?;
1417
1418 let mut out = Vec::with_capacity(count);
1419 let mut cursor = body;
1420 for _ in 0..count {
1421 let first = *cursor.as_bytes().first().ok_or_else(|| {
1422 AttachmentError::Decode("truncated group: expected another signature".into())
1423 })?;
1424 let hs = indexer_hard_len(first);
1425 if cursor.len() < hs {
1426 return Err(AttachmentError::Decode("truncated siger hard code".into()));
1427 }
1428 let code = &cursor[..hs];
1429 let (fs, os) = indexer_sizage(code)
1430 .ok_or_else(|| AttachmentError::Decode(format!("unknown indexer code {code:?}")))?;
1431 if cursor.len() < fs {
1432 return Err(AttachmentError::Decode(format!(
1433 "insufficient bytes for {code} siger: need {fs}, have {}",
1434 cursor.len()
1435 )));
1436 }
1437 let (siger_qb64, remainder) = cursor.split_at(fs);
1438 cursor = remainder;
1439
1440 let siger = Siger::new(None, None, None, None, None, None, Some(siger_qb64), None)
1441 .map_err(|e| AttachmentError::Decode(format!("Siger: {e}")))?;
1442 let prior_index = (os > 0).then(|| siger.ondex());
1445
1446 out.push(IndexedSignature {
1447 index: siger.index(),
1448 prior_index,
1449 sig: siger.raw(),
1450 });
1451 }
1452
1453 Ok((out, cursor))
1454}
1455
1456#[derive(Debug, thiserror::Error)]
1458#[non_exhaustive]
1459pub enum AttachmentError {
1460 #[error("attachment encode: {0}")]
1462 Encode(String),
1463 #[error("attachment decode: {0}")]
1465 Decode(String),
1466}
1467
1468const B64_ALPHA: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1470
1471fn encode_count_b64(count: usize) -> Result<String, AttachmentError> {
1472 if count >= 64 * 64 {
1473 return Err(AttachmentError::Encode(format!(
1474 "count {count} exceeds 2-char base64url max (4095)"
1475 )));
1476 }
1477 let hi = B64_ALPHA[(count >> 6) & 0x3f] as char;
1478 let lo = B64_ALPHA[count & 0x3f] as char;
1479 Ok(format!("{hi}{lo}"))
1480}
1481
1482fn decode_count_b64(s: &str) -> Result<usize, AttachmentError> {
1483 let mut it = s.chars();
1484 let hi = it
1485 .next()
1486 .and_then(b64_index)
1487 .ok_or_else(|| AttachmentError::Decode(format!("invalid count hi char: {s:?}")))?;
1488 let lo = it
1489 .next()
1490 .and_then(b64_index)
1491 .ok_or_else(|| AttachmentError::Decode(format!("invalid count lo char: {s:?}")))?;
1492 Ok((hi << 6) | lo)
1493}
1494
1495fn b64_index(c: char) -> Option<usize> {
1496 B64_ALPHA.iter().position(|&b| b as char == c)
1497}
1498
1499#[cfg(test)]
1500#[allow(clippy::unwrap_used)]
1501mod tests {
1502 use super::*;
1503
1504 #[test]
1505 fn attachment_roundtrip_single_sig() {
1506 let sigs = vec![IndexedSignature {
1507 index: 0,
1508 prior_index: None,
1509 sig: vec![0x42u8; 64],
1510 }];
1511 let bytes = serialize_attachment(&sigs).unwrap();
1512 let s = std::str::from_utf8(&bytes).unwrap();
1513 assert!(s.starts_with("-AAB"), "expected -AAB prefix, got {s:?}");
1514 let back = parse_attachment(&bytes).unwrap();
1515 assert_eq!(back.len(), 1);
1516 assert_eq!(back[0].index, 0);
1517 assert_eq!(back[0].sig, vec![0x42u8; 64]);
1518 }
1519
1520 #[test]
1521 fn attachment_roundtrip_three_sigs() {
1522 let sigs = vec![
1523 IndexedSignature {
1524 index: 0,
1525 prior_index: None,
1526 sig: vec![0x01u8; 64],
1527 },
1528 IndexedSignature {
1529 index: 1,
1530 prior_index: None,
1531 sig: vec![0x02u8; 64],
1532 },
1533 IndexedSignature {
1534 index: 2,
1535 prior_index: None,
1536 sig: vec![0x03u8; 64],
1537 },
1538 ];
1539 let bytes = serialize_attachment(&sigs).unwrap();
1540 let s = std::str::from_utf8(&bytes).unwrap();
1541 assert!(s.starts_with("-AAD"), "expected -AAD prefix, got {s:?}");
1542 let back = parse_attachment(&bytes).unwrap();
1543 assert_eq!(back.len(), 3);
1544 for (i, sig) in back.iter().enumerate() {
1545 assert_eq!(sig.index, i as u32);
1546 }
1547 }
1548
1549 #[test]
1550 fn attachment_empty() {
1551 let bytes = serialize_attachment(&[]).unwrap();
1552 assert_eq!(bytes, b"-AAA");
1553 let back = parse_attachment(&bytes).unwrap();
1554 assert!(back.is_empty());
1555 }
1556
1557 #[test]
1558 fn keri_sequence_serializes_as_hex() {
1559 assert_eq!(
1560 serde_json::to_string(&KeriSequence::new(0)).unwrap(),
1561 "\"0\""
1562 );
1563 assert_eq!(
1564 serde_json::to_string(&KeriSequence::new(10)).unwrap(),
1565 "\"a\""
1566 );
1567 assert_eq!(
1568 serde_json::to_string(&KeriSequence::new(255)).unwrap(),
1569 "\"ff\""
1570 );
1571 }
1572
1573 #[test]
1574 fn keri_sequence_deserializes_from_hex() {
1575 let s: KeriSequence = serde_json::from_str("\"0\"").unwrap();
1576 assert_eq!(s.value(), 0);
1577 let s: KeriSequence = serde_json::from_str("\"a\"").unwrap();
1578 assert_eq!(s.value(), 10);
1579 let s: KeriSequence = serde_json::from_str("\"ff\"").unwrap();
1580 assert_eq!(s.value(), 255);
1581 }
1582
1583 #[test]
1584 fn keri_sequence_rejects_invalid_hex() {
1585 assert!(serde_json::from_str::<KeriSequence>("\"not_hex\"").is_err());
1586 }
1587
1588 #[test]
1589 fn icp_event_always_serializes_d_a_c() {
1590 use crate::types::{CesrKey, Threshold, VersionString};
1591 let icp = IcpEvent {
1592 v: VersionString::placeholder(),
1593 d: Said::default(),
1594 i: Prefix::new_unchecked("ETest123".to_string()),
1595 s: KeriSequence::new(0),
1596 kt: Threshold::Simple(1),
1597 k: vec![CesrKey::new_unchecked("DKey123".to_string())],
1598 nt: Threshold::Simple(1),
1599 n: vec![Said::new_unchecked("ENext456".to_string())],
1600 bt: Threshold::Simple(0),
1601 b: vec![],
1602 c: vec![],
1603 a: vec![],
1604 };
1605 let json = serde_json::to_string(&icp).unwrap();
1606 assert!(json.contains("\"d\":"), "d must always be present");
1608 assert!(json.contains("\"a\":"), "a must always be present");
1609 assert!(json.contains("\"c\":"), "c must always be present");
1610 assert!(!json.contains("\"x\":"), "empty x must be omitted");
1612 assert!(json.contains("\"s\":\"0\""), "s must serialize as hex");
1613 }
1614
1615 #[test]
1616 fn event_enum_deserializes_by_t_field() {
1617 let json = r#"{"v":"KERI10JSON000000_","t":"icp","d":"ETestSaid","i":"E123","s":"0","kt":"1","k":["DKey"],"nt":"1","n":["ENext"],"bt":"0","b":[]}"#;
1618 let event: Event = serde_json::from_str(json).unwrap();
1619 assert!(event.is_inception());
1620 assert_eq!(event.sequence().value(), 0);
1621 }
1622
1623 #[test]
1624 fn digest_seal_roundtrips() {
1625 let seal = Seal::digest("EDigest123");
1626 let json = serde_json::to_string(&seal).unwrap();
1627 assert_eq!(json, r#"{"d":"EDigest123"}"#);
1628 let parsed: Seal = serde_json::from_str(&json).unwrap();
1629 assert_eq!(seal, parsed);
1630 }
1631
1632 #[test]
1633 fn seal_event_location_roundtrips() {
1634 let seal = Seal::EventLocation {
1636 i: Prefix::new_unchecked("EPrefix".to_string()),
1637 s: KeriSequence::new(3),
1638 p: Said::new_unchecked("EPrior".to_string()),
1639 t: "ixn".to_string(),
1640 d: Said::new_unchecked("EDigest".to_string()),
1641 };
1642 let json = serde_json::to_string(&seal).unwrap();
1643 let parsed: Seal = serde_json::from_str(&json).unwrap();
1644 assert_eq!(seal, parsed);
1645 assert_eq!(parsed.digest_value().map(|d| d.as_str()), Some("EDigest"));
1646 }
1647
1648 #[test]
1649 fn seal_rejects_malformed_i_s() {
1650 let r: Result<Seal, _> = serde_json::from_str(r#"{"i":"EPrefix","s":"3"}"#);
1653 assert!(r.is_err(), "malformed {{i,s}} seal must be rejected");
1654 let r2: Result<Seal, _> = serde_json::from_str(r#"{"zz":"x"}"#);
1656 assert!(r2.is_err());
1657 }
1658
1659 #[test]
1660 fn key_event_seal_roundtrips() {
1661 let seal = Seal::key_event(
1662 Prefix::new_unchecked("EPrefix".to_string()),
1663 KeriSequence::new(1),
1664 Said::new_unchecked("ESaid".to_string()),
1665 );
1666 let json = serde_json::to_string(&seal).unwrap();
1667 let parsed: Seal = serde_json::from_str(&json).unwrap();
1668 assert_eq!(seal, parsed);
1669 }
1670
1671 #[test]
1672 fn indexed_signature_serde_roundtrip() {
1673 let sig = IndexedSignature {
1674 index: 2,
1675 prior_index: None,
1676 sig: vec![0xab; 64],
1677 };
1678 let json = serde_json::to_string(&sig).unwrap();
1679 assert!(json.contains("\"index\":2"));
1680 assert!(
1681 !json.contains("prior_index"),
1682 "a None prior_index must be omitted from the wire: {json}"
1683 );
1684 let parsed: IndexedSignature = serde_json::from_str(&json).unwrap();
1685 assert_eq!(parsed, sig);
1686 }
1687
1688 #[test]
1689 fn signed_event_accessors() {
1690 use crate::types::{CesrKey, Threshold, VersionString};
1691 let icp = IcpEvent {
1692 v: VersionString::placeholder(),
1693 d: Said::new_unchecked("ESAID123".to_string()),
1694 i: Prefix::new_unchecked("EPrefix".to_string()),
1695 s: KeriSequence::new(0),
1696 kt: Threshold::Simple(1),
1697 k: vec![CesrKey::new_unchecked("DKey".to_string())],
1698 nt: Threshold::Simple(1),
1699 n: vec![Said::new_unchecked("ENext".to_string())],
1700 bt: Threshold::Simple(0),
1701 b: vec![],
1702 c: vec![],
1703 a: vec![],
1704 };
1705 let signed = SignedEvent::new(
1706 Event::Icp(icp),
1707 vec![IndexedSignature {
1708 index: 0,
1709 prior_index: None,
1710 sig: vec![0u8; 64],
1711 }],
1712 );
1713 assert_eq!(signed.said().as_str(), "ESAID123");
1714 assert_eq!(signed.sequence().value(), 0);
1715 assert_eq!(signed.prefix().as_str(), "EPrefix");
1716 assert_eq!(signed.signatures.len(), 1);
1717 assert_eq!(signed.signatures[0].index, 0);
1718 }
1719
1720 #[test]
1721 fn seal_digest_value() {
1722 let seal = Seal::digest("ETest123");
1723 assert_eq!(seal.digest_value().unwrap().as_str(), "ETest123");
1724 let latest = Seal::LatestEstablishment {
1725 i: Prefix::new_unchecked("EPrefix".to_string()),
1726 };
1727 assert!(latest.digest_value().is_none());
1728 }
1729}