1use cesrox::{group::Group, ParsedData};
2use serde::{ser::SerializeStruct, Deserialize, Serialize};
3
4use super::{msg::KeriEvent, serializer::to_string, signature::Nontransferable};
5#[cfg(feature = "query")]
6use crate::query::{query_event::SignedQueryMessage, reply_event::SignedReply};
7use crate::{
8 error::Error,
9 event::{
10 receipt::Receipt,
11 sections::seal::{EventSeal, SourceSeal},
12 KeyEvent,
13 },
14 prefix::{IdentifierPrefix, IndexedSignature},
15 state::{EventSemantics, IdentifierState},
16};
17
18#[cfg(feature = "mailbox")]
19use crate::mailbox::exchange::SignedExchange;
20
21#[derive(Clone, Debug, PartialEq)]
22pub enum Message {
23 Notice(Notice),
24 #[cfg(feature = "query")]
25 Op(Op),
26}
27
28#[derive(Clone, Debug, PartialEq)]
29pub enum Notice {
30 Event(SignedEventMessage),
31 NontransferableRct(SignedNontransferableReceipt),
34 TransferableRct(SignedTransferableReceipt),
35}
36
37#[cfg(any(feature = "query", feature = "oobi"))]
38#[derive(Clone, Debug, PartialEq)]
39pub enum Op {
40 #[cfg(feature = "mailbox")]
41 Exchange(SignedExchange),
42 #[cfg(feature = "query")]
43 Reply(SignedReply),
44 #[cfg(feature = "query")]
45 Query(SignedQueryMessage),
46}
47
48impl From<Message> for ParsedData {
49 fn from(message: Message) -> Self {
50 match message {
51 Message::Notice(notice) => ParsedData::from(notice),
52 #[cfg(any(feature = "query", feature = "oobi"))]
53 Message::Op(op) => ParsedData::from(op),
54 }
55 }
56}
57
58impl From<Notice> for ParsedData {
59 fn from(notice: Notice) -> Self {
60 match notice {
61 Notice::Event(event) => ParsedData::from(&event),
62 Notice::NontransferableRct(rct) => ParsedData::from(rct),
63 Notice::TransferableRct(rct) => ParsedData::from(rct),
64 }
65 }
66}
67
68#[cfg(feature = "query")]
69impl From<Op> for ParsedData {
70 fn from(op: Op) -> Self {
71 match op {
72 #[cfg(feature = "query")]
73 Op::Reply(ksn) => ParsedData::from(ksn),
74 #[cfg(feature = "query")]
75 Op::Query(qry) => ParsedData::from(qry),
76 #[cfg(feature = "mailbox")]
79 Op::Exchange(exn) => ParsedData::from(exn),
80 }
81 }
82}
83
84impl Message {
85 pub fn to_cesr(&self) -> Result<Vec<u8>, Error> {
86 ParsedData::from(self.clone())
87 .to_cesr()
88 .map_err(|_e| Error::CesrError)
89 }
90
91 pub fn get_prefix(&self) -> IdentifierPrefix {
92 match self {
93 Message::Notice(notice) => notice.get_prefix(),
94 #[cfg(any(feature = "query", feature = "oobi"))]
95 Message::Op(op) => op.get_prefix(),
96 }
97 }
98}
99
100impl Notice {
101 pub fn get_prefix(&self) -> IdentifierPrefix {
102 match self {
103 Notice::Event(ev) => ev.event_message.data.get_prefix(),
104 Notice::NontransferableRct(rct) => rct.body.prefix.clone(),
105 Notice::TransferableRct(rct) => rct.body.prefix.clone(),
106 }
107 }
108}
109
110#[cfg(feature = "query")]
111impl Op {
112 pub fn get_prefix(&self) -> IdentifierPrefix {
113 match self {
114 Op::Reply(reply) => reply.reply.get_prefix(),
115 Op::Query(qry) => qry.prefix(),
116 #[cfg(feature = "mailbox")]
117 Op::Exchange(exn) => exn.exchange_message.data.data.get_prefix(),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Deserialize)]
125pub struct SignedEventMessage {
126 pub event_message: KeriEvent<KeyEvent>,
127 #[serde(skip_serializing)]
128 pub signatures: Vec<IndexedSignature>,
129 #[serde(skip_serializing)]
130 pub witness_receipts: Option<Vec<Nontransferable>>,
131 #[serde(skip_serializing)]
132 pub delegator_seal: Option<SourceSeal>,
133}
134
135impl Serialize for SignedEventMessage {
136 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
137 where
138 S: serde::Serializer,
139 {
140 if serializer.is_human_readable() {
142 let mut em = serializer.serialize_struct("EventMessage", 4)?;
143 em.serialize_field("", &self.event_message)?;
144 let att_sigs = Group::IndexedControllerSignatures(
145 self.signatures
146 .iter()
147 .map(|sig| sig.clone().into())
148 .collect(),
149 );
150 em.serialize_field("-", &att_sigs.to_cesr_str())?;
151 if let Some(ref receipts) = self.witness_receipts {
152 let att_receipts = receipts
153 .iter()
154 .map(|rct| match rct {
155 Nontransferable::Indexed(indexed) => {
156 let signatures =
157 indexed.iter().map(|sig| (sig.clone()).into()).collect();
158 Group::IndexedWitnessSignatures(signatures).to_cesr_str()
159 }
160 Nontransferable::Couplet(couplets) => {
161 let couples = couplets
162 .iter()
163 .map(|(bp, sp)| ((bp.clone()).into(), (sp.clone()).into()))
164 .collect();
165 Group::NontransReceiptCouples(couples).to_cesr_str()
166 }
167 })
168 .collect::<Vec<_>>()
169 .join("");
170 em.serialize_field("", &att_receipts)?;
171 }
172 if let Some(ref seal) = self.delegator_seal {
173 let att_seal =
174 Group::SourceSealCouples(vec![(seal.sn, seal.digest.said.clone().into())]);
175 em.serialize_field("", &att_seal.to_cesr_str())?;
176 }
177
178 em.end()
179 } else {
181 let mut em = serializer.serialize_struct("SignedEventMessage", 4)?;
182 em.serialize_field("event_message", &self.event_message)?;
183 em.serialize_field("signatures", &self.signatures)?;
184 em.serialize_field("witness_receipts", &self.witness_receipts)?;
185 em.serialize_field("delegator_seal", &self.delegator_seal)?;
186 em.end()
187 }
188 }
189}
190
191impl PartialEq for SignedEventMessage {
192 fn eq(&self, other: &Self) -> bool {
193 self.event_message == other.event_message && self.signatures == other.signatures
194 }
195}
196
197impl SignedEventMessage {
198 pub fn new(
199 message: &KeriEvent<KeyEvent>,
200 sigs: Vec<IndexedSignature>,
201 witness_receipts: Option<Vec<Nontransferable>>,
202 delegator_seal: Option<SourceSeal>,
203 ) -> Self {
204 Self {
205 event_message: message.clone(),
206 signatures: sigs,
207 witness_receipts,
208 delegator_seal,
209 }
210 }
211
212 pub fn encode(&self) -> Result<Vec<u8>, Error> {
213 Ok(to_string(&self)?.as_bytes().to_vec())
214 }
215}
216
217impl EventSemantics for SignedEventMessage {
218 fn apply_to(&self, state: IdentifierState) -> Result<IdentifierState, Error> {
219 self.event_message.apply_to(state)
220 }
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
230pub struct SignedTransferableReceipt {
231 pub body: Receipt,
232 pub validator_seal: EventSeal,
233 pub signatures: Vec<IndexedSignature>,
234}
235
236impl SignedTransferableReceipt {
237 pub fn new(message: Receipt, event_seal: EventSeal, sigs: Vec<IndexedSignature>) -> Self {
238 Self {
239 body: message,
240 validator_seal: event_seal,
241 signatures: sigs,
242 }
243 }
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
253pub struct SignedNontransferableReceipt {
254 pub body: Receipt,
255 pub signatures: Vec<Nontransferable>,
258}
259
260impl SignedNontransferableReceipt {
261 pub fn new(message: &Receipt, signatures: Vec<Nontransferable>) -> Self {
262 Self {
263 body: message.clone(),
264 signatures,
265 }
266 }
267}
268
269#[cfg(test)]
270pub mod tests {
271 use std::convert::TryFrom;
272
273 use cesrox::{parse, ParsedData};
274
275 use crate::{
276 actor::prelude::Message,
277 event_message::{signature::Nontransferable, signed_event_message::Notice},
278 };
279
280 #[test]
281 fn test_stream1() {
282 let stream = br#"{"v":"KERI10JSON00012b_","t":"icp","d":"ECwI3rbyMMCCBrjBcZW-qIh4SFeY1ri6fl6nFNZ6_LPn","i":"DEzolW_U9CTatBFey9LL9e4_FOekoAJdTbReEstNEl-D","s":"0","kt":"1","k":["DEzolW_U9CTatBFey9LL9e4_FOekoAJdTbReEstNEl-D"],"nt":"1","n":["EL0nWR23_LnKW6OAXJauX2oz6N2V_QZfWeT4tsK-y3jZ"],"bt":"0","b":[],"c":[],"a":[]}-AABAAB7Ro77feCA8A0B632ThEzVKGHwUrEx-TGyV8VdXKZvxPivaWqR__Exa7n02sjJkNlrQcOqs7cXsJ6IDopxkbEC"#;
284
285 let parsed = parse(stream).unwrap().1;
286 let msg = Message::try_from(parsed).unwrap();
287 assert!(matches!(msg, Message::Notice(Notice::Event(_))));
288
289 match msg {
290 Message::Notice(Notice::Event(signed_event)) => {
291 assert_eq!(
292 signed_event.event_message.encode().unwrap().len(),
293 signed_event.event_message.serialization_info.size
294 );
295
296 let serialized_again = signed_event.encode();
297 assert!(serialized_again.is_ok());
298 let stringified = String::from_utf8(serialized_again.unwrap()).unwrap();
299 assert_eq!(stream, stringified.as_bytes());
300 }
301 _ => assert!(false),
302 }
303 }
304
305 #[test]
306 fn test_stream2() {
307 let stream = br#"{"v":"KERI10JSON0001e7_","t":"icp","d":"EBfxc4RiVY6saIFmUfEtETs1FcqmktZW88UkbnOg0Qen","i":"EBfxc4RiVY6saIFmUfEtETs1FcqmktZW88UkbnOg0Qen","s":"0","kt":"2","k":["DErocgXD2RGSyvn3MObcx59jeOsEQhv2TqHirVkzrp0Q","DFXLiTjiRdSBPLL6hLa0rskIxk3dh4XwJLfctkJFLRSS","DE9YgIQVgpLwocTVrG8tidKScsQSMWwLWywNC48fhq4f"],"nt":"2","n":["EDJk5EEpC4-tQ7YDwBiKbpaZahh1QCyQOnZRF7p2i8k8","EAXfDjKvUFRj-IEB_o4y-Y_qeJAjYfZtOMD9e7vHNFss","EN8l6yJC2PxribTN0xfri6bLz34Qvj-x3cNwcV3DvT2m"],"bt":"0","b":[],"c":[],"a":[]}-AADAAD4SyJSYlsQG22MGXzRGz2PTMqpkgOyUfq7cS99sC2BCWwdVmEMKiTEeWe5kv-l_d9auxdadQuArLtAGEArW8wEABD0z_vQmFImZXfdR-0lclcpZFfkJJJNXDcUNrf7a-mGsxNLprJo-LROwDkH5m7tVrb-a1jcor2dHD9Jez-r4bQIACBFeU05ywfZycLdR0FxCvAR9BfV9im8tWe1DglezqJLf-vHRQSChY1KafbYNc96hYYpbuN90WzuCRMgV8KgRsEC"#;
309 let parsed = parse(stream).unwrap().1;
310 let msg = Message::try_from(parsed);
311 assert!(msg.is_ok());
312 assert!(matches!(msg, Ok(Message::Notice(Notice::Event(_)))));
313
314 match msg.unwrap() {
315 Message::Notice(Notice::Event(signed_event)) => {
316 assert_eq!(
317 signed_event.event_message.encode().unwrap().len(),
318 signed_event.event_message.serialization_info.size
319 );
320 let serialized_again = signed_event.encode();
321 assert!(serialized_again.is_ok());
322 let stringified = String::from_utf8(serialized_again.unwrap()).unwrap();
323 assert_eq!(stream, stringified.as_bytes())
324 }
325 _ => assert!(false),
326 }
327 }
328
329 #[test]
330 fn test_deserialize_signed_receipt() {
331 let trans_receipt_event = br#"{"v":"KERI10JSON000091_","t":"rct","d":"EsZuhYAPBDnexP3SOl9YsGvWBrYkjYcRjomUYmCcLAYY","i":"EsZuhYAPBDnexP3SOl9YsGvWBrYkjYcRjomUYmCcLAYY","s":"0"}-FABE7pB5IKuaYh3aIWKxtexyYFhpSjDNTEGSQuxeJbWiylg0AAAAAAAAAAAAAAAAAAAAAAAE7pB5IKuaYh3aIWKxtexyYFhpSjDNTEGSQuxeJbWiylg-AABAAlIts3z2kNyis9l0Pfu54HhVN_yZHEV7NWIVoSTzl5IABelbY8xi7VRyW42ZJvBaaFTGtiqwMOywloVNpG_ZHAQ"#;
333 let parsed_trans_receipt = parse(trans_receipt_event).unwrap().1;
334 let msg = Message::try_from(parsed_trans_receipt);
335 assert!(matches!(
336 msg,
337 Ok(Message::Notice(Notice::TransferableRct(_)))
338 ));
339 assert!(msg.is_ok());
340
341 let nontrans_rcp = br#"{"v":"KERI10JSON000091_","t":"rct","d":"E77aKmmdHtYKuJeBOYWRHbi8C6dYqzG-ESfdvlUAptlo","i":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","s":"2"}-CABB389hKezugU2LFKiFVbitoHAxXqJh6HQ8Rn9tH7fxd680Bpx_cu_UoMtD0ES-bS9Luh-b2A_AYmM3PmVNfgFrFXls4IE39-_D14dS46NEMqCf0vQmqDcQmhY-UOpgoyFS2Bw"#;
343 let parsed_nontrans_receipt = parse(nontrans_rcp).unwrap().1;
344 let msg = Message::try_from(parsed_nontrans_receipt);
345 assert!(msg.is_ok());
346 assert!(matches!(
347 msg,
348 Ok(Message::Notice(Notice::NontransferableRct(_)))
349 ));
350
351 let witness_receipts = br#"{"v":"KERI10JSON000091_","t":"rct","d":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","i":"EHz9RXAr9JiJn-3wkBvsUo1Qq3hvMQPaITxzcfJND8NM","s":"0"}-BADAAdgQkf11JTyF2WVA1Vji1ZhXD8di4AJsfro-sN_jURM1SUioeOleik7w8lkDldKtg0-Nr1X32V9Q8tk8RvBGxDgABZmkRun-qNliRA8WR2fIUnVeB8eFLF7aLFtn2hb31iW7wYSYafR0kT3fV_r1wNNdjm9dkBw-_2xsxThTGfO5UAwACRGJiRPFe4ClvpqZL3LHcEAeT396WVrYV10EaTdt0trINT8rPbz96deSFT32z3myNPVwLlNcq4FzIaQCooM2HDQ"#;
353 let parsed_witness_receipt: ParsedData = parse(witness_receipts).unwrap().1;
354
355 let msg = Message::try_from(parsed_witness_receipt);
356 assert!(msg.is_ok());
357 if let Ok(Message::Notice(Notice::NontransferableRct(rct))) = msg {
358 match &rct.signatures[0] {
359 Nontransferable::Indexed(indexed) => {
360 assert_eq!(3, indexed.len());
361 }
362 Nontransferable::Couplet(_) => {
363 unreachable!()
364 }
365 };
366 } else {
367 assert!(false)
368 };
369 }
370
371 #[cfg(feature = "mailbox")]
372 #[test]
373 fn test_deserialize_signed_exchange() {
374 use crate::event_message::signed_event_message::Op;
375 let exn_event = br#"{"v":"KERI10JSON0002f1_","t":"exn","d":"EBLqTGJXK8ViUGXMOO8_LXbetpjJX8CY_SbA134RIZmf","dt":"2022-10-25T09:53:04.119676+00:00","r":"/fwd","q":{"pre":"EKYLUMmNPZeEs77Zvclf0bSN5IN-mLfLpx2ySb-HDlk4","topic":"multisig"},"a":{"v":"KERI10JSON000215_","t":"icp","d":"EC61gZ9lCKmHAS7U5ehUfEbGId5rcY0D7MirFZHDQcE2","i":"EC61gZ9lCKmHAS7U5ehUfEbGId5rcY0D7MirFZHDQcE2","s":"0","kt":"2","k":["DOZlWGPfDHLMf62zSFzE8thHmnQUOgA3_Y-KpOyF9ScG","DHGb2qY9WwZ1sBnC9Ip0F-M8QjTM27ftI-3jTGF9mc6K"],"nt":"2","n":["EBvD5VIVvf6NpP9GRmTqu_Cd1KN0RKrKNfPJ-uhIxurj","EHlpcaxffvtcpoUUMTc6tpqAVtb2qnOYVk_3HRsZ34PH"],"bt":"3","b":["BBilc4-L3tFUnfM_wJr4S4OJanAv_VmF_dJNN6vkf2Ha","BLskRTInXnMxWaGqcpSyMgo0nYbalW99cGZESrz3zapM","BIKKuvBwpmDVA4Ds-EpL5bt9OqPzWPja2LigFYZN2YfX"],"c":[],"a":[]}}-HABEJccSRTfXYF6wrUVuenAIHzwcx3hJugeiJsEKmndi5q1-AABAAArUSuSpts5zDQ7CgPcy305IxhAG8lOjf-r_d5yYQXp18OD9No_gd2McOOjGWMfjyLVjDK529pQcbvNv9Uwc6gH-LAZ5AABAA-a-AABAABYHc_lpuYF3SPNWvyPjzek7yquw69Csc6pLv5vrXHkFAFDcwNNTVxq7ZpxpqOO0CAIS-9Qj1zMor-cwvMHAmkE"#;
376
377 let parsed_exn = parse(exn_event).unwrap().1;
378 let msg = Message::try_from(parsed_exn).unwrap();
379 assert!(matches!(msg, Message::Op(Op::Exchange(_))));
380 assert_eq!(msg.to_cesr().unwrap(), exn_event);
381 }
382}