Skip to main content

ossa_core/store/ecg/
v0.rs

1use ossa_crdt::{time::CausalState, CRDT};
2use ossa_typeable::Typeable;
3use rand::Rng;
4use serde::{
5    de::{MapAccess, Visitor},
6    ser::{SerializeStruct, Serializer},
7    Deserialize, Serialize,
8};
9use std::{
10    collections::{BTreeMap, BTreeSet},
11    fmt::Debug,
12    marker::PhantomData,
13};
14
15use crate::{
16    store::ecg::{self, ECGBody, ECGHeader},
17    time::{CausalTime, ConcretizeTime},
18    util,
19};
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Typeable, Deserialize, Serialize)]
22pub struct HeaderId<Hash>(Hash);
23
24// TODO: Move this to the right location.
25/// An ECG header.
26#[derive(Clone, Debug, Deserialize, Serialize)]
27// #[derive(Clone, Typeable)]
28// #[tag = "v1"]
29pub struct Header<Hash> {
30    // , T> {
31    /// A nonce to randomize the header.
32    nonce: u8,
33
34    /// The header ids of our parents in the ECG graph.
35    parent_ids: Vec<HeaderId<Hash>>,
36
37    /// The number of operations in the corresponding body.
38    /// The maximum number of operations is 255.
39    operations_count: u8,
40
41    /// The hash of (batched) operations in the corresponding body.
42    /// TODO: Eventually this should be of the encrypted body..
43    operations_hash: Hash,
44    // // TODO: DeviceId and UserId of device signing? Maybe whole auth chain?
45    // phantom: PhantomData<T>,
46}
47
48#[derive(Debug)]
49pub struct Body<Hash, SerializedOp> {
50    /// The operations in this ECG body.
51    /// Invariant: <= 256 operations
52    operations: Vec<SerializedOp>, // <CausalTime<T::Time>>>,
53    phantom: PhantomData<fn(Hash)>,
54}
55
56// TODO: Define CBOR or rkyv properly
57impl<Hash, SerializedOp> Serialize for Body<Hash, SerializedOp>
58where
59    SerializedOp: Serialize,
60{
61    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62    where
63        S: Serializer,
64    {
65        let mut s = serializer.serialize_struct("Body", 1)?;
66        s.serialize_field("operations", &self.operations)?;
67        s.end()
68    }
69}
70
71impl<'d, Hash, SerializedOp> Deserialize<'d> for Body<Hash, SerializedOp>
72where
73    SerializedOp: Deserialize<'d>,
74{
75    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
76    where
77        D: serde::Deserializer<'d>,
78    {
79        struct SVisitor<Hash, T>(PhantomData<(Hash, T)>);
80
81        #[derive(Deserialize)]
82        #[serde(field_identifier, rename_all = "lowercase")]
83        enum Field {
84            Operations,
85        }
86
87        impl<'d, Hash, SerializedOp> Visitor<'d> for SVisitor<Hash, SerializedOp>
88        where
89            SerializedOp: Deserialize<'d>,
90        {
91            type Value = Body<Hash, SerializedOp>;
92
93            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
94                formatter.write_str("struct Body")
95            }
96
97            fn visit_map<M>(self, mut m: M) -> Result<Body<Hash, SerializedOp>, M::Error>
98            where
99                M: MapAccess<'d>,
100            {
101                let mut operations = None;
102                while let Some(key) = m.next_key()? {
103                    match key {
104                        Field::Operations => {
105                            if operations.is_some() {
106                                return Err(serde::de::Error::duplicate_field("operations"));
107                            }
108                            operations = Some(m.next_value()?);
109                        }
110                    }
111                }
112
113                let operations =
114                    operations.ok_or_else(|| serde::de::Error::missing_field("operations"))?;
115                Ok(Body {
116                    operations,
117                    phantom: PhantomData,
118                })
119            }
120        }
121
122        deserializer.deserialize_struct("Body", &["operations"], SVisitor(PhantomData))
123    }
124}
125
126impl<
127        Hash: Clone + Copy + Debug + Ord + util::Hash,
128        // T : CRDT<Time = OperationId<HeaderId<Hash>>>,
129    > ECGHeader for Header<Hash>
130where
131    // <T as CRDT>::Op: Serialize,
132    Hash: Serialize, // TODO
133{
134    type HeaderId = HeaderId<Hash>;
135    // type Body = Body<Hash, T>;
136
137    fn get_parent_ids(&self) -> &[HeaderId<Hash>] {
138        &self.parent_ids
139    }
140
141    fn get_header_id(&self) -> HeaderId<Hash> {
142        HeaderId(tmp_hash(self))
143    }
144
145    fn validate_header(&self, header_id: HeaderId<Hash>) -> bool {
146        // TODO: Actually check this.
147        true
148    }
149
150    // // TODO: Move this to ECG state?
151    // fn new_header(parents: BTreeSet<Self::HeaderId>, body: &Body<Hash, T>) -> Self {
152    //     let mut rng = rand::thread_rng();
153    //     let nonce = rng.gen();
154
155    //     // Sort parent headers.
156    //     let parents = parents.into_iter().collect();
157
158    //     // TODO: Check for hash conflicts and generate another nonce?
159
160    //     Header {
161    //         parent_ids: parents,
162    //         nonce,
163    //         operations_count: body.operations_count(),
164    //         operations_hash: body.get_hash(),
165    //         phantom: PhantomData,
166    //     }
167    // }
168
169    // // Replace OperationId<H> with T::Time? Or add another associated type to ECGHeader?
170    // fn zip_operations_with_time<A>(&self, body: Self::Body) -> Vec<(A::Time, A::Op)> where A: CRDT {
171    //     // let times = self.get_operation_times(&body);
172    //     // let ops = body.operations();
173    //     // times.into_iter().zip(ops).collect();
174    //     todo!()
175
176    // }
177
178    // fn get_operation_times<A>(&self, body: &Self::Body) -> Vec<A::Time> where A: CRDT {
179    //     // let header_id = Some(self.get_header_id());
180    //     // let operations_c = body.operations_count();
181    //     // (0..operations_c)
182    //     //     .map(move |i| OperationId {
183    //     //         header_id,
184    //     //         operation_position: i,
185    //     //     })
186    //     //     .collect();
187    //     todo!()
188    // }
189}
190
191const MAX_OPERATION_COUNT: usize = 256;
192
193// impl<Hash, T> ECGBody<T::Op, <T::Op as ConcretizeTime<T::Time>>::Serialized> for Body<Hash, <T::Op as ConcretizeTime<T::Time>>::Serialized>
194impl<Hash, Op> ECGBody<Op, Op::Serialized> for Body<Hash, Op::Serialized>
195where
196    // T: CRDT<Time = OperationId<HeaderId<Hash>>>,
197    Op: ConcretizeTime<HeaderId<Hash>>,
198    Op::Serialized: Serialize,
199    // T::Op: ConcretizeTime<OperationId<HeaderId<Hash>>>,
200    // <T::Op as ConcretizeTime<T::Time>>::Serialized: Serialize,
201    Hash: Clone + Copy + Debug + Ord + util::Hash + Serialize,
202{
203    type Header = Header<Hash>;
204
205    fn new_body(operations: Vec<Op::Serialized>) -> Self {
206        if operations.len() > MAX_OPERATION_COUNT {
207            panic!("Exceeded the maximum number of batched operations.");
208        }
209
210        Body {
211            operations,
212            phantom: PhantomData,
213        }
214    }
215
216    fn operations(self, header_id: HeaderId<Hash>) -> impl Iterator<Item = Op> {
217        // let header_id = Some(header_id);
218        self.operations.into_iter().map(move |op| {
219            // let operation_id = OperationId {
220            //     header_id,
221            //     operation_position: i as u8,
222            // };
223            Op::concretize_time(op, header_id)
224        })
225        // self.operations.into_iter().map(move |op| op.concretize_time(|t| {
226        //     match t {
227        //         CausalTime::Time(t) => t,
228        //         CausalTime::Current { operation_position } => OperationId {
229        //             header_id: Some(header_id),
230        //             operation_position,
231        //         }
232        //     }
233        // }))
234    }
235
236    fn operations_count(&self) -> u8 {
237        self.operations
238            .len()
239            .try_into()
240            .expect("Unreachable: Length is bound by MAX_OPERATION_COUNT.")
241    }
242
243    fn new_header(&self, parents: BTreeSet<<Self::Header as ECGHeader>::HeaderId>) -> Self::Header {
244        let mut rng = rand::thread_rng();
245        let nonce = rng.gen();
246
247        // Sort parent headers.
248        let parents = parents.into_iter().collect();
249
250        // TODO: Check for hash conflicts and generate another nonce?
251
252        Header {
253            parent_ids: parents,
254            nonce,
255            operations_count: <Self as ECGBody<Op, Op::Serialized>>::operations_count(self),
256            operations_hash: self.get_hash(),
257            // phantom: PhantomData,
258        }
259    }
260
261    // fn zip_operations_with_time(
262    //     self,
263    //     header: &Self::Header,
264    // ) -> Vec<(<T as CRDT>::Time, <T as CRDT>::Op<T::Time>)> {
265    //     let times = self.get_operation_times(header);
266    //     let ops = self.operations(header.get_header_id());
267    //     times.into_iter().zip(ops).collect()
268    // }
269
270    // fn get_operation_times(&self, header: &Self::Header) -> Vec<<T as CRDT>::Time> {
271    //     let header_id = Some(header.get_header_id());
272    //     let operations_c = self.operations_count();
273    //     (0..operations_c)
274    //         .map(move |i| OperationId {
275    //             header_id,
276    //             operation_position: i,
277    //         })
278    //         .collect()
279    // }
280}
281
282impl<Hash: util::Hash, SerializedOp> Body<Hash, SerializedOp>
283where
284    SerializedOp: Serialize,
285{
286    fn get_hash(&self) -> Hash {
287        tmp_hash(self)
288    }
289}
290
291// TODO: Standardize how to hash/serialize. XXX
292fn tmp_hash<T: Serialize, Hash: util::Hash>(x: &T) -> Hash {
293    // JP: Better way to do this? Just serialize once?
294    let mut h = Hash::new();
295    let serialized = serde_cbor::ser::to_vec(&x).unwrap();
296    Hash::update(&mut h, serialized);
297    Hash::finalize(h)
298}
299
300// OperationID's are header ids and index (HeaderId, u8)
301// TODO: Move this to ossa-crdt::time??
302#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Typeable)]
303pub struct OperationId<HeaderId> {
304    pub header_id: Option<HeaderId>, // None when in the initial state?
305    pub operation_position: u8,
306}
307
308impl<HeaderId> ConcretizeTime<HeaderId> for OperationId<HeaderId> {
309    type Serialized = CausalTime<OperationId<HeaderId>>;
310
311    fn concretize_time(src: Self::Serialized, current_header: HeaderId) -> Self {
312        match src {
313            CausalTime::Current { operation_position } => OperationId {
314                header_id: Some(current_header),
315                operation_position,
316            },
317            CausalTime::Time(t) => t,
318        }
319    }
320}
321
322impl<HeaderId> OperationId<HeaderId> {
323    pub fn new(header_id: Option<HeaderId>, operation_position: u8) -> Self {
324        OperationId {
325            header_id,
326            operation_position,
327        }
328    }
329}
330
331impl<Header: ECGHeader, T: CRDT> CausalState for ecg::State<Header, T> {
332    type Time = OperationId<Header::HeaderId>;
333
334    fn happens_before(&self, a: &Self::Time, b: &Self::Time) -> bool {
335        if a.header_id == b.header_id {
336            a.operation_position < b.operation_position
337        } else {
338            if let Some(a_header_id) = &a.header_id {
339                if let Some(b_header_id) = &b.header_id {
340                    self.is_ancestor_of(a_header_id, b_header_id)
341                        .expect("Invariant violated. Unknown header id.")
342                } else {
343                    // a.header_id.is_some() && b.header_id == None
344                    false
345                }
346            } else {
347                // a.header_id == None
348                true
349            }
350        }
351    }
352}
353
354#[derive(Clone, Debug)]
355pub struct TestHeader<T> {
356    pub header_id: u32,
357    pub parent_ids: Vec<u32>,
358    pub phantom: PhantomData<T>,
359}
360
361pub struct TestBody<SerializedOp> {
362    operations: Vec<SerializedOp>,
363}
364
365/*
366impl<T: CRDT<Time = u32>> ECGBody<T> for TestBody<<T::Op as ConcretizeTime<u32>>::Serialized>
367where
368    T::Op: ConcretizeTime<u32>,
369//     T::Op<CausalTime<T::Time>>: Serialize + ConcretizeTime<CausalTime<T::Time>, T::Time, Target<T::Time> = T::Op<T::Time>>,
370{
371    type Header = TestHeader<T>;
372
373    fn new_body(operations: Vec<<T::Op as ConcretizeTime<u32>>::Serialized>) -> Self {
374        TestBody { operations }
375    }
376
377    fn operations(self, header_id: u32) -> impl Iterator<Item = T::Op> {
378        self.operations.into_iter().map(move |op| T::Op::concretize_time(op, header_id))
379        // self.operations.into_iter().map(|op| op.concretize_time(|t| {
380        //     match t {
381        //         CausalTime::Time(t) => t,
382        //         CausalTime::Current { operation_position } => todo!(),
383        //     }
384        // }))
385    }
386
387    fn operations_count(&self) -> u8 {
388        self.operations
389            .len()
390            .try_into()
391            .expect("Unreachable: Length is bound by MAX_OPERATION_COUNT.")
392    }
393
394    // fn zip_operations_with_time(
395    //     self,
396    //     header: &Self::Header,
397    // ) -> Vec<(<T as CRDT>::Time, <T as CRDT>::Op<T::Time>)> {
398    //     todo!()
399    // }
400
401    // fn get_operation_times(&self, header: &Self::Header) -> Vec<<T as CRDT>::Time> {
402    //     todo!()
403    // }
404
405    fn new_header(&self, parents: BTreeSet<<Self::Header as ECGHeader>::HeaderId>) -> Self::Header {
406        todo!()
407    }
408}
409*/
410
411// For testing, just have the header store the parent ids.
412impl<A: CRDT> ECGHeader for TestHeader<A> {
413    type HeaderId = u32;
414    // type Body = TestBody<A>;
415
416    fn get_parent_ids(&self) -> &[u32] {
417        &self.parent_ids
418    }
419
420    fn get_header_id(&self) -> u32 {
421        self.header_id
422    }
423
424    fn validate_header(&self, header_id: Self::HeaderId) -> bool {
425        true
426    }
427
428    // fn new_header(parents: BTreeSet<Self::HeaderId>, _body: &Self::Body) -> Self {
429    //     todo!()
430    // }
431
432    // // JP: TODO: Move this to ECGBody?
433    // fn zip_operations_with_time<T>(&self, body: Self::Body) -> Vec<(T::Time, T::Op)>
434    // where
435    //     T: CRDT,
436    //     Self::Body: ECGBody<T>,
437    // {
438    //     let v: Vec<_> = todo!();
439    //     v
440    // }
441
442    // fn get_operation_times<T>(&self, body: &Self::Body) -> Vec<T::Time> where T: CRDT, {
443    //     let v: Vec<_> = todo!();
444    //     v
445    // }
446}