cap_common/
transaction.rs

1use crate::did::EventHash;
2use certified_vars::HashTree::Pruned;
3use certified_vars::{AsHashTree, Hash, HashTree};
4use ic_kit::candid::{CandidType, Deserialize, Nat};
5use ic_kit::Principal;
6use serde::Serialize;
7use sha2::{Digest, Sha256};
8use std::collections::BTreeSet;
9use std::convert::TryInto;
10
11#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
12pub struct Event {
13    /// The timestamp in ms.
14    pub time: u64,
15    /// The caller that initiated the call on the token contract.
16    pub caller: Principal,
17    /// The operation that took place.
18    pub operation: String,
19    /// Details of the transaction.
20    pub details: Vec<(String, DetailValue)>,
21}
22
23#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
24pub struct IndefiniteEvent {
25    /// The caller that initiated the call on the token contract.
26    pub caller: Principal,
27    /// The operation that took place.
28    pub operation: String,
29    /// Details of the transaction.
30    pub details: Vec<(String, DetailValue)>,
31}
32
33#[derive(CandidType, Serialize, Deserialize, Clone, Debug)]
34pub enum DetailValue {
35    True,
36    False,
37    U64(u64),
38    I64(i64),
39    Float(f64),
40    Text(String),
41    Principal(Principal),
42    #[serde(with = "serde_bytes")]
43    Slice(Vec<u8>),
44    Vec(Vec<DetailValue>),
45    TokenIdU64(u64),
46}
47
48impl Event {
49    /// Return a set containing all of the Principal IDs involved in an event.
50    #[inline]
51    pub fn extract_principal_ids(&self) -> BTreeSet<&Principal> {
52        let mut principals = BTreeSet::new();
53
54        principals.insert(&self.caller);
55
56        fn visit<'a>(principals: &mut BTreeSet<&'a Principal>, value: &'a DetailValue) {
57            match value {
58                DetailValue::Principal(p) => {
59                    principals.insert(p);
60                }
61                DetailValue::Vec(items) => {
62                    for item in items {
63                        visit(principals, item);
64                    }
65                }
66                _ => {}
67            }
68        }
69
70        for (_, value) in &self.details {
71            visit(&mut principals, value);
72        }
73
74        principals
75    }
76
77    /// Return a set containing all of the token ids involved in an event.
78    #[inline]
79    pub fn extract_token_ids(&self) -> BTreeSet<u64> {
80        let mut tokens = BTreeSet::new();
81
82        fn visit(tokens: &mut BTreeSet<u64>, value: &DetailValue) {
83            match value {
84                DetailValue::TokenIdU64(id) => {
85                    tokens.insert(*id);
86                }
87                DetailValue::Vec(items) => {
88                    for item in items {
89                        visit(tokens, item);
90                    }
91                }
92                _ => {}
93            }
94        }
95
96        for (_, value) in &self.details {
97            visit(&mut tokens, value);
98        }
99
100        tokens
101    }
102
103    /// Compute the hash for the given event.
104    pub fn hash(&self) -> EventHash {
105        let mut h = domain_sep(&self.operation);
106
107        h.update(&self.time.to_be_bytes() as &[u8]);
108        let caller = self.caller.as_slice();
109        h.update(&caller.len().to_be_bytes() as &[u8]);
110        h.update(caller);
111
112        fn hash_value(h: &mut Sha256, value: &DetailValue) {
113            match value {
114                DetailValue::True => {
115                    h.update(&[0]);
116                }
117                DetailValue::False => {
118                    h.update(&[1]);
119                }
120                DetailValue::U64(val) => {
121                    let bytes = val.to_be_bytes();
122                    h.update(&[2]);
123                    h.update(&bytes.len().to_be_bytes() as &[u8]);
124                    h.update(bytes);
125                }
126                DetailValue::I64(val) => {
127                    let bytes = val.to_be_bytes();
128                    h.update(&[3]);
129                    h.update(&bytes.len().to_be_bytes() as &[u8]);
130                    h.update(bytes);
131                }
132                DetailValue::Float(val) => {
133                    let bytes = val.to_be_bytes();
134                    h.update(&[4]);
135                    h.update(&bytes.len().to_be_bytes() as &[u8]);
136                    h.update(bytes);
137                }
138                DetailValue::Text(val) => {
139                    let bytes = val.as_str().as_bytes();
140                    h.update(&[5]);
141                    h.update(&bytes.len().to_be_bytes() as &[u8]);
142                    h.update(bytes);
143                }
144                DetailValue::Principal(val) => {
145                    let bytes = val.as_slice();
146                    h.update(&[6]);
147                    h.update(&bytes.len().to_be_bytes() as &[u8]);
148                    h.update(bytes);
149                }
150                DetailValue::Slice(val) => {
151                    let bytes = val.as_slice();
152                    h.update(&[7]);
153                    h.update(&bytes.len().to_be_bytes() as &[u8]);
154                    h.update(bytes);
155                }
156                DetailValue::Vec(val) => {
157                    h.update(&[8]);
158                    h.update(&val.len().to_be_bytes() as &[u8]);
159                    for item in val.iter() {
160                        hash_value(h, item);
161                    }
162                }
163                DetailValue::TokenIdU64(val) => {
164                    let bytes = val.to_be_bytes();
165                    h.update(&[9]);
166                    h.update(&bytes.len().to_be_bytes() as &[u8]);
167                    h.update(bytes);
168                }
169            }
170        }
171
172        for (key, value) in &self.details {
173            h.update(&key.len().to_be_bytes() as &[u8]);
174            h.update(key.as_str().as_bytes());
175            hash_value(&mut h, value);
176        }
177
178        h.finalize().into()
179    }
180}
181
182impl Into<IndefiniteEvent> for Event {
183    fn into(self) -> IndefiniteEvent {
184        IndefiniteEvent {
185            caller: self.caller,
186            operation: self.operation,
187            details: self.details,
188        }
189    }
190}
191
192impl IndefiniteEvent {
193    /// Convert an indefinite event to a definite one by adding the token and time fields.
194    #[inline]
195    pub fn to_event(self, time: u64) -> Event {
196        Event {
197            time,
198            caller: self.caller,
199            operation: self.operation,
200            details: self.details,
201        }
202    }
203}
204
205impl From<u64> for DetailValue {
206    fn from(num: u64) -> Self {
207        Self::U64(num)
208    }
209}
210
211impl TryInto<u64> for DetailValue {
212    type Error = ();
213
214    fn try_into(self) -> Result<u64, Self::Error> {
215        if let Self::U64(num) = self {
216            Ok(num)
217        } else {
218            Err(())
219        }
220    }
221}
222
223impl From<i64> for DetailValue {
224    fn from(num: i64) -> Self {
225        Self::I64(num)
226    }
227}
228
229impl TryInto<i64> for DetailValue {
230    type Error = ();
231
232    fn try_into(self) -> Result<i64, Self::Error> {
233        if let Self::I64(num) = self {
234            Ok(num)
235        } else {
236            Err(())
237        }
238    }
239}
240
241impl From<f64> for DetailValue {
242    fn from(float: f64) -> Self {
243        Self::Float(float)
244    }
245}
246
247impl TryInto<f64> for DetailValue {
248    type Error = ();
249
250    fn try_into(self) -> Result<f64, Self::Error> {
251        if let Self::Float(num) = self {
252            Ok(num)
253        } else {
254            Err(())
255        }
256    }
257}
258
259impl From<String> for DetailValue {
260    fn from(string: String) -> Self {
261        Self::Text(string)
262    }
263}
264
265impl TryInto<String> for DetailValue {
266    type Error = ();
267
268    fn try_into(self) -> Result<String, Self::Error> {
269        if let Self::Text(val) = self {
270            Ok(val)
271        } else {
272            Err(())
273        }
274    }
275}
276
277impl From<Principal> for DetailValue {
278    fn from(principal: Principal) -> Self {
279        Self::Principal(principal)
280    }
281}
282
283impl TryInto<Principal> for DetailValue {
284    type Error = ();
285
286    fn try_into(self) -> Result<Principal, Self::Error> {
287        if let Self::Principal(principal) = self {
288            Ok(principal)
289        } else {
290            Err(())
291        }
292    }
293}
294
295impl From<Nat> for DetailValue {
296    fn from(nat: Nat) -> Self {
297        let mut vec = vec![];
298
299        nat.encode(&mut vec).unwrap();
300
301        DetailValue::Slice(vec)
302    }
303}
304
305impl TryInto<Nat> for DetailValue {
306    type Error = ();
307
308    fn try_into(self) -> Result<Nat, Self::Error> {
309        if let Self::Slice(nat) = self {
310            if let Ok(nat) = Nat::parse(&nat) {
311                Ok(nat)
312            } else {
313                Err(())
314            }
315        } else {
316            Err(())
317        }
318    }
319}
320
321fn domain_sep(s: &str) -> sha2::Sha256 {
322    let buf: [u8; 1] = [s.len() as u8];
323    let mut h = sha2::Sha256::new();
324    h.update(&buf[..]);
325    h.update(s.as_bytes());
326    h
327}
328
329impl AsHashTree for Event {
330    fn root_hash(&self) -> Hash {
331        self.hash()
332    }
333
334    fn as_hash_tree(&self) -> HashTree<'_> {
335        Pruned(self.hash())
336    }
337}
338
339// TODO(qti3e) Test