ng_repo/
types.rs

1// Copyright (c) 2022-2024 Niko Bonnieure, Par le Peuple, NextGraph.org developers
2// All rights reserved.
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6// at your option. All files in the project carrying such
7// notice may not be copied, modified, or distributed except
8// according to those terms.
9
10//! NextGraph Repo types
11//!
12//! Corresponds to the BARE schema
13
14use core::fmt;
15use std::hash::Hash;
16
17use once_cell::sync::OnceCell;
18use serde::{Deserialize, Serialize};
19use threshold_crypto::serde_impl::SerdeSecret;
20use zeroize::{Zeroize, ZeroizeOnDrop};
21
22use crate::errors::NgError;
23use crate::utils::{
24    decode_key, decode_priv_key, dh_pubkey_array_from_ed_pubkey_slice,
25    dh_pubkey_from_ed_pubkey_slice, ed_privkey_to_ed_pubkey, from_ed_privkey_to_dh_privkey,
26    random_key,
27};
28
29//
30// COMMON TYPES
31//
32
33/// 32-byte Blake3 hash digest
34pub type Blake3Digest32 = [u8; 32];
35
36/// Hash digest
37#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
38pub enum Digest {
39    Blake3Digest32(Blake3Digest32),
40}
41
42impl Digest {
43    pub fn from_slice(slice: [u8; 32]) -> Digest {
44        Digest::Blake3Digest32(slice)
45    }
46    pub fn slice(&self) -> &[u8; 32] {
47        match self {
48            Self::Blake3Digest32(o) => o,
49        }
50    }
51}
52
53impl fmt::Display for Digest {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        let ser = serde_bare::to_vec(&self).unwrap();
56        write!(f, "{}", base64_url::encode(&ser))
57    }
58}
59
60impl From<&Vec<u8>> for Digest {
61    fn from(ser: &Vec<u8>) -> Self {
62        let hash = blake3::hash(ser.as_slice());
63        Digest::Blake3Digest32(hash.as_bytes().clone())
64    }
65}
66
67impl From<&[u8; 32]> for Digest {
68    fn from(ser: &[u8; 32]) -> Self {
69        let hash = blake3::hash(ser);
70        Digest::Blake3Digest32(hash.as_bytes().clone())
71    }
72}
73
74impl From<&PubKey> for Digest {
75    fn from(key: &PubKey) -> Self {
76        key.slice().into()
77    }
78}
79
80/// ChaCha20 symmetric key
81pub type ChaCha20Key = [u8; 32];
82
83/// Symmetric cryptographic key
84#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
85pub enum SymKey {
86    ChaCha20Key(ChaCha20Key),
87}
88
89impl SymKey {
90    pub fn slice(&self) -> &[u8; 32] {
91        match self {
92            SymKey::ChaCha20Key(o) => o,
93        }
94    }
95    pub fn random() -> Self {
96        SymKey::ChaCha20Key(random_key())
97    }
98    pub fn from_array(array: [u8; 32]) -> Self {
99        SymKey::ChaCha20Key(array)
100    }
101    pub fn nil() -> Self {
102        SymKey::ChaCha20Key([0; 32])
103    }
104    #[cfg(any(test, feature = "testing"))]
105    pub fn dummy() -> Self {
106        SymKey::ChaCha20Key([0; 32])
107    }
108}
109
110impl fmt::Display for SymKey {
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        let ser = serde_bare::to_vec(&self).unwrap();
113        write!(f, "{}", base64_url::encode(&ser))
114    }
115}
116
117impl TryFrom<&[u8]> for SymKey {
118    type Error = NgError;
119    fn try_from(buf: &[u8]) -> Result<Self, NgError> {
120        let sym_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
121        let sym_key = SymKey::ChaCha20Key(sym_key_array);
122        Ok(sym_key)
123    }
124}
125
126/// Curve25519 public key Edwards form
127pub type Ed25519PubKey = [u8; 32];
128
129/// Curve25519 public key Montgomery form
130pub type X25519PubKey = [u8; 32];
131
132/// Curve25519 private key Edwards form
133pub type Ed25519PrivKey = [u8; 32];
134
135/// Curve25519 private key Montgomery form
136pub type X25519PrivKey = [u8; 32];
137
138/// Public key
139#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
140pub enum PubKey {
141    Ed25519PubKey(Ed25519PubKey),
142    X25519PubKey(X25519PubKey),
143}
144
145impl Default for PubKey {
146    fn default() -> Self {
147        Self::nil()
148    }
149}
150
151impl PubKey {
152    pub fn to_dh(self) -> X25519PubKey {
153        match self {
154            Self::X25519PubKey(x) => x,
155            _ => panic!("cannot call to_dh on an Edward key"),
156        }
157    }
158    pub fn slice(&self) -> &[u8; 32] {
159        match self {
160            PubKey::Ed25519PubKey(o) | PubKey::X25519PubKey(o) => o,
161        }
162    }
163    pub fn to_dh_from_ed(&self) -> PubKey {
164        match self {
165            PubKey::Ed25519PubKey(ed) => dh_pubkey_from_ed_pubkey_slice(ed),
166            _ => panic!(
167                "there is no need to convert a Montgomery key to Montgomery. it is already one. check your code"
168            ),
169        }
170    }
171    // pub fn dh_from_ed_slice(slice: &[u8]) -> PubKey {
172    //     dh_pubkey_from_ed_pubkey_slice(slice)
173    // }
174    pub fn to_dh_slice(&self) -> [u8; 32] {
175        match self {
176            PubKey::Ed25519PubKey(o) => dh_pubkey_array_from_ed_pubkey_slice(o),
177            _ => panic!("can only convert an edward key to montgomery"),
178        }
179    }
180
181    pub fn nil() -> Self {
182        PubKey::Ed25519PubKey([0u8; 32])
183    }
184
185    pub fn to_hash_string(&self) -> String {
186        let ser = serde_bare::to_vec(&self).unwrap();
187        let hash = blake3::hash(&ser);
188        base64_url::encode(&hash.as_bytes())
189    }
190}
191
192impl fmt::Display for PubKey {
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        let ser = serde_bare::to_vec(&self).unwrap();
195        write!(f, "{}", base64_url::encode(&ser))
196    }
197}
198
199impl TryFrom<&str> for PubKey {
200    type Error = NgError;
201    fn try_from(str: &str) -> Result<Self, NgError> {
202        decode_key(str)
203    }
204}
205
206/// Private key
207#[derive(Clone, Zeroize, ZeroizeOnDrop, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
208pub enum PrivKey {
209    Ed25519PrivKey(Ed25519PrivKey),
210    X25519PrivKey(X25519PrivKey),
211}
212
213#[allow(deprecated)]
214impl Default for PrivKey {
215    fn default() -> Self {
216        Self::nil()
217    }
218}
219
220impl PrivKey {
221    pub fn slice(&self) -> &[u8; 32] {
222        match self {
223            PrivKey::Ed25519PrivKey(o) | PrivKey::X25519PrivKey(o) => o,
224        }
225    }
226    pub fn to_pub(&self) -> PubKey {
227        match self {
228            PrivKey::Ed25519PrivKey(_) => ed_privkey_to_ed_pubkey(self),
229            _ => panic!("X25519PrivKey to pub not implemented"),
230        }
231    }
232
233    pub fn nil() -> PrivKey {
234        PrivKey::Ed25519PrivKey([0u8; 32])
235    }
236
237    #[cfg(any(test, feature = "testing"))]
238    pub fn dummy() -> PrivKey {
239        PrivKey::Ed25519PrivKey([0u8; 32])
240    }
241
242    pub fn to_dh(&self) -> PrivKey {
243        from_ed_privkey_to_dh_privkey(self)
244    }
245
246    pub fn random_ed() -> Self {
247        PrivKey::Ed25519PrivKey(random_key())
248    }
249}
250
251impl From<[u8; 32]> for PrivKey {
252    fn from(buf: [u8; 32]) -> Self {
253        let priv_key = PrivKey::Ed25519PrivKey(buf);
254        priv_key
255    }
256}
257
258impl TryFrom<&[u8]> for PrivKey {
259    type Error = NgError;
260    fn try_from(buf: &[u8]) -> Result<Self, NgError> {
261        let priv_key_array = *slice_as_array!(buf, [u8; 32]).ok_or(NgError::InvalidKey)?;
262        let priv_key = PrivKey::Ed25519PrivKey(priv_key_array);
263        Ok(priv_key)
264    }
265}
266
267impl TryFrom<&str> for PrivKey {
268    type Error = NgError;
269    fn try_from(str: &str) -> Result<Self, NgError> {
270        decode_priv_key(str)
271    }
272}
273
274impl fmt::Display for PrivKey {
275    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
276        let ser = serde_bare::to_vec(&self).unwrap();
277        write!(f, "{}", base64_url::encode(&ser))
278    }
279}
280
281/// Ed25519 signature
282pub type Ed25519Sig = [[u8; 32]; 2];
283
284/// Cryptographic signature
285#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
286pub enum Sig {
287    Ed25519Sig(Ed25519Sig),
288}
289
290impl fmt::Display for Sig {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        match self {
293            Self::Ed25519Sig(ed) => {
294                write!(
295                    f,
296                    "{} {}",
297                    base64_url::encode(&ed[0]),
298                    base64_url::encode(&ed[1])
299                )
300            }
301        }
302    }
303}
304
305impl Sig {
306    pub fn nil() -> Self {
307        Sig::Ed25519Sig([[0; 32]; 2])
308    }
309}
310
311/// Timestamp: absolute time in minutes since 2022-02-22 22:22 UTC
312pub type Timestamp = u32;
313
314pub const EPOCH_AS_UNIX_TIMESTAMP: u64 = 1645568520;
315
316/// Relative time (e.g. delay from current time)
317#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
318pub enum RelTime {
319    Seconds(u8),
320    Minutes(u8),
321    Hours(u8),
322    Days(u8),
323    None,
324}
325
326impl fmt::Display for RelTime {
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        match self {
329            Self::Seconds(s) => writeln!(f, "{} sec.", s),
330            Self::Minutes(s) => writeln!(f, "{} min.", s),
331            Self::Hours(s) => writeln!(f, "{} h.", s),
332            Self::Days(s) => writeln!(f, "{} d.", s),
333            Self::None => writeln!(f, "None"),
334        }
335    }
336}
337
338/// Bloom filter (variable size)
339#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
340pub struct BloomFilterV0 {
341    /// Filter
342    #[serde(with = "serde_bytes")]
343    pub f: Vec<u8>,
344}
345
346#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
347pub enum BloomFilter {
348    V0(BloomFilterV0),
349}
350
351impl BloomFilter {
352    pub fn filter(&self) -> &Vec<u8> {
353        match self {
354            Self::V0(v0) => &v0.f,
355        }
356    }
357}
358
359//
360// REPOSITORY TYPES
361//
362
363/// RepoId is a PubKey
364pub type RepoId = PubKey;
365
366/// RepoHash is the BLAKE3 Digest over the RepoId
367pub type RepoHash = Digest;
368
369impl From<RepoId> for RepoHash {
370    fn from(id: RepoId) -> Self {
371        Digest::Blake3Digest32(*blake3::hash(id.slice()).as_bytes())
372    }
373}
374
375// impl From<RepoHash> for String {
376//     fn from(id: RepoHash) -> Self {
377//         hex::encode(to_vec(&id).unwrap())
378//     }
379// }
380
381/// Topic ID: public key of the topic
382pub type TopicId = PubKey;
383
384/// User ID: user account for broker and member of a repo
385pub type UserId = PubKey;
386
387/// BranchId is a PubKey
388pub type BranchId = PubKey;
389
390/// Block ID: BLAKE3 hash over the serialized BlockContent (contains encrypted content)
391pub type BlockId = Digest;
392
393pub type BlockKey = SymKey;
394
395/// Block reference
396#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
397pub struct BlockRef {
398    /// Block ID
399    pub id: BlockId,
400
401    /// Key for decrypting the Block
402    pub key: BlockKey,
403}
404
405impl Default for BlockId {
406    fn default() -> Self {
407        Self::nil()
408    }
409}
410
411impl BlockId {
412    #[cfg(any(test, feature = "testing"))]
413    pub fn dummy() -> Self {
414        Digest::Blake3Digest32([0u8; 32])
415    }
416
417    pub fn nil() -> Self {
418        Digest::Blake3Digest32([0u8; 32])
419    }
420}
421
422impl BlockRef {
423    #[cfg(any(test, feature = "testing"))]
424    pub fn dummy() -> Self {
425        BlockRef {
426            id: Digest::Blake3Digest32([0u8; 32]),
427            key: SymKey::ChaCha20Key([0u8; 32]),
428        }
429    }
430
431    pub fn nil() -> Self {
432        BlockRef {
433            id: Digest::Blake3Digest32([0u8; 32]),
434            key: SymKey::ChaCha20Key([0u8; 32]),
435        }
436    }
437
438    pub fn from_id_key(id: BlockId, key: BlockKey) -> Self {
439        BlockRef { id, key }
440    }
441
442    pub fn nuri(&self) -> String {
443        format!(":j:{}:k:{}", self.id, self.key)
444    }
445}
446
447impl From<BlockRef> for (BlockId, BlockKey) {
448    fn from(blockref: BlockRef) -> (BlockId, BlockKey) {
449        (blockref.id.clone(), blockref.key.clone())
450    }
451}
452
453impl From<(&BlockId, &BlockKey)> for BlockRef {
454    fn from(id_key: (&BlockId, &BlockKey)) -> Self {
455        BlockRef {
456            id: id_key.0.clone(),
457            key: id_key.1.clone(),
458        }
459    }
460}
461
462impl fmt::Display for BlockRef {
463    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
464        write!(f, "{} {}", self.id, self.key)
465    }
466}
467
468/// Object ID
469pub type ObjectId = BlockId;
470
471/// Object Key
472pub type ObjectKey = BlockKey;
473
474/// Object reference
475pub type ObjectRef = BlockRef;
476
477/// Read capability (for a commit, branch, whole repo, or store)
478///
479/// For a store: A ReadCap to the root repo of the store
480/// For a repo: A reference to the latest RootBranch definition commit
481/// For a branch: A reference to the latest Branch definition commit
482/// For a commit or object, the ObjectRef is itself the read capability
483pub type ReadCap = ObjectRef;
484
485/// Read capability secret (for a commit, branch, whole repo, or store)
486///
487/// it is already included in the ReadCap (it is the key part of the reference)
488pub type ReadCapSecret = ObjectKey;
489
490/// Write capability secret (for a whole repo)
491pub type RepoWriteCapSecret = SymKey;
492
493/// Write capability secret (for a branch's topic)
494pub type BranchWriteCapSecret = PrivKey;
495
496//TODO: PermaCap (involves sending an InboxPost to some verifiers)
497
498//
499// IDENTITY, SITE, STORE, OVERLAY common types
500//
501
502// /// List of Identity types
503// #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
504// pub enum Identity {
505//     OrgSite(PubKey),
506//     IndividualSite(PubKey),
507//     OrgPublicStore(PubKey),
508//     OrgProtectedStore(PubKey),
509//     OrgPrivateStore(PubKey),
510//     IndividualPublicStore(PubKey),
511//     IndividualProtectedStore(PubKey),
512//     IndividualPrivateStore(PubKey),
513// }
514
515pub type OuterOverlayId = Digest;
516
517pub type InnerOverlayId = Digest;
518
519/// Overlay ID
520///
521/// - for outer overlays that need to be discovered by public key:
522///   BLAKE3 hash over the public key of the store repo
523/// - for inner overlays:
524///   BLAKE3 keyed hash over the public key of the store repo
525///   - key: BLAKE3 derive_key ("NextGraph Overlay ReadCapSecret BLAKE3 key", store repo's overlay's branch ReadCapSecret)
526///   except for Dialog Overlays where the Hash is computed from 2 secrets.
527#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
528pub enum OverlayId {
529    Outer(OuterOverlayId),
530    Inner(InnerOverlayId),
531    Global,
532}
533
534impl fmt::Display for OverlayId {
535    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
536        let overlay_ser = serde_bare::to_vec(&self).unwrap();
537        write!(f, "{}", base64_url::encode(&overlay_ser))
538    }
539}
540
541impl OverlayId {
542    // pub fn inner_from_store(store: &Store) -> OverlayId {
543    //     Self::inner(store.id(), store.get_store_overlay_branch_readcap_secret())
544    // }
545    pub fn inner(
546        store_id: &PubKey,
547        store_overlay_branch_readcap_secret: &ReadCapSecret,
548    ) -> OverlayId {
549        let store_id = serde_bare::to_vec(store_id).unwrap();
550        let mut store_overlay_branch_readcap_secret_ser =
551            serde_bare::to_vec(store_overlay_branch_readcap_secret).unwrap();
552        let mut key: [u8; 32] = blake3::derive_key(
553            "NextGraph Overlay ReadCapSecret BLAKE3 key",
554            store_overlay_branch_readcap_secret_ser.as_slice(),
555        );
556        let key_hash = blake3::keyed_hash(&key, &store_id);
557        store_overlay_branch_readcap_secret_ser.zeroize();
558        key.zeroize();
559        OverlayId::Inner(Digest::from_slice(*key_hash.as_bytes()))
560    }
561
562    pub fn outer(store_id: &PubKey) -> OverlayId {
563        let store_id = serde_bare::to_vec(store_id).unwrap();
564        OverlayId::Outer((&store_id).into())
565    }
566    #[cfg(any(test, feature = "testing"))]
567    pub fn dummy() -> OverlayId {
568        OverlayId::Outer(Digest::dummy())
569    }
570    pub fn nil() -> OverlayId {
571        OverlayId::Outer(Digest::nil())
572    }
573
574    pub fn is_inner(&self) -> bool {
575        match self {
576            Self::Inner(_) => true,
577            _ => false,
578        }
579    }
580
581    pub fn is_outer(&self) -> bool {
582        match self {
583            Self::Outer(_) => true,
584            _ => false,
585        }
586    }
587}
588
589/// List of Store Overlay types
590#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
591pub enum StoreOverlayV0 {
592    PublicStore(PubKey),
593    ProtectedStore(PubKey),
594    PrivateStore(PubKey),
595    Group(PubKey),
596    Dialog(Digest),
597}
598
599#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
600pub enum StoreOverlay {
601    V0(StoreOverlayV0),
602    Own(BranchId), // The repo is a store, so the overlay can be derived from its own ID. In this case, the branchId of the `overlay` branch is entered here.
603}
604
605impl fmt::Display for StoreOverlay {
606    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
607        match self {
608            Self::V0(v0) => {
609                write!(f, "StoreOverlay V0 ")?;
610                match v0 {
611                    StoreOverlayV0::PublicStore(k) => writeln!(f, "PublicStore: {}", k),
612                    StoreOverlayV0::ProtectedStore(k) => writeln!(f, "ProtectedStore: {}", k),
613                    StoreOverlayV0::PrivateStore(k) => writeln!(f, "PrivateStore: {}", k),
614                    StoreOverlayV0::Group(k) => writeln!(f, "Group: {}", k),
615                    StoreOverlayV0::Dialog(k) => writeln!(f, "Dialog: {}", k),
616                }
617            }
618            Self::Own(b) => writeln!(f, "Own: {}", b),
619        }
620    }
621}
622
623impl StoreOverlay {
624    pub fn from_store_repo(overlay_branch: BranchId) -> StoreOverlay {
625        StoreOverlay::Own(overlay_branch)
626    }
627
628    pub fn overlay_id_for_read_purpose(&self) -> OverlayId {
629        match self {
630            StoreOverlay::V0(StoreOverlayV0::PublicStore(id))
631            | StoreOverlay::V0(StoreOverlayV0::ProtectedStore(id))
632            | StoreOverlay::V0(StoreOverlayV0::PrivateStore(id))
633            | StoreOverlay::V0(StoreOverlayV0::Group(id)) => OverlayId::outer(id),
634            StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone()),
635            StoreOverlay::Own(_) => unimplemented!(),
636        }
637    }
638
639    pub fn overlay_id_for_write_purpose(
640        &self,
641        store_overlay_branch_readcap_secret: ReadCapSecret,
642    ) -> OverlayId {
643        match self {
644            StoreOverlay::V0(StoreOverlayV0::PublicStore(id))
645            | StoreOverlay::V0(StoreOverlayV0::ProtectedStore(id))
646            | StoreOverlay::V0(StoreOverlayV0::PrivateStore(id))
647            | StoreOverlay::V0(StoreOverlayV0::Group(id)) => {
648                OverlayId::inner(id, &store_overlay_branch_readcap_secret)
649            }
650            StoreOverlay::V0(StoreOverlayV0::Dialog(d)) => OverlayId::Inner(d.clone()),
651            StoreOverlay::Own(_) => unimplemented!(),
652        }
653    }
654}
655
656impl From<&StoreRepo> for StoreOverlay {
657    fn from(store_repo: &StoreRepo) -> Self {
658        match store_repo {
659            StoreRepo::V0(v0) => match v0 {
660                StoreRepoV0::PublicStore(id) => {
661                    StoreOverlay::V0(StoreOverlayV0::PublicStore(id.clone()))
662                }
663                StoreRepoV0::ProtectedStore(id) => {
664                    StoreOverlay::V0(StoreOverlayV0::ProtectedStore(id.clone()))
665                }
666                StoreRepoV0::PrivateStore(id) => {
667                    StoreOverlay::V0(StoreOverlayV0::PrivateStore(id.clone()))
668                }
669                StoreRepoV0::Group(id) => StoreOverlay::V0(StoreOverlayV0::Group(id.clone())),
670                StoreRepoV0::Dialog((_, d)) => StoreOverlay::V0(StoreOverlayV0::Dialog(d.clone())),
671            },
672        }
673    }
674}
675
676/// List of Store Root Repo types
677#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
678pub enum StoreRepoV0 {
679    PublicStore(RepoId),
680    ProtectedStore(RepoId),
681    PrivateStore(RepoId),
682    Group(RepoId),
683    Dialog((RepoId, Digest)),
684}
685
686#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
687pub enum StoreRepo {
688    V0(StoreRepoV0),
689}
690
691impl fmt::Display for StoreRepo {
692    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693        write!(
694            f,
695            "StoreRepo V0 {} {}",
696            match self {
697                StoreRepo::V0(v0) => match v0 {
698                    StoreRepoV0::PublicStore(_) => "PublicStore",
699                    StoreRepoV0::ProtectedStore(_) => "ProtectedStore",
700                    StoreRepoV0::PrivateStore(_) => "PrivateStore",
701                    StoreRepoV0::Group(_) => "Group",
702                    StoreRepoV0::Dialog(_) => "Dialog",
703                },
704            },
705            self.repo_id()
706        )
707    }
708}
709
710impl StoreRepo {
711    pub fn repo_id(&self) -> &RepoId {
712        match self {
713            Self::V0(v0) => match v0 {
714                StoreRepoV0::PublicStore(id)
715                | StoreRepoV0::ProtectedStore(id)
716                | StoreRepoV0::PrivateStore(id)
717                | StoreRepoV0::Group(id)
718                | StoreRepoV0::Dialog((id, _)) => id,
719            },
720        }
721    }
722    #[cfg(any(test, feature = "testing"))]
723    #[allow(deprecated)]
724    pub fn dummy_public_v0() -> Self {
725        let store_pubkey = PubKey::nil();
726        StoreRepo::V0(StoreRepoV0::PublicStore(store_pubkey))
727    }
728    #[cfg(any(test, feature = "testing"))]
729    pub fn dummy_with_key(repo_pubkey: PubKey) -> Self {
730        StoreRepo::V0(StoreRepoV0::PublicStore(repo_pubkey))
731    }
732
733    pub fn nil() -> Self {
734        let store_pubkey = PubKey::nil();
735        StoreRepo::V0(StoreRepoV0::PublicStore(store_pubkey))
736    }
737
738    pub fn new_private(repo_pubkey: PubKey) -> Self {
739        StoreRepo::V0(StoreRepoV0::PrivateStore(repo_pubkey))
740    }
741
742    pub fn outer_overlay(&self) -> OverlayId {
743        self.overlay_id_for_read_purpose()
744    }
745
746    pub fn overlay_id_for_read_purpose(&self) -> OverlayId {
747        let store_overlay: StoreOverlay = self.into();
748        store_overlay.overlay_id_for_read_purpose()
749        //OverlayId::outer(self.repo_id())
750    }
751
752    pub fn is_private(&self) -> bool {
753        match self {
754            Self::V0(StoreRepoV0::PrivateStore(_)) => true,
755            _ => false,
756        }
757    }
758
759    // pub fn overlay_id_for_storage_purpose(
760    //     &self,
761    //     store_overlay_branch_readcap_secret: Option<ReadCapSecret>,
762    // ) -> OverlayId {
763    //     match self {
764    //         Self::V0(StoreRepoV0::PublicStore(id))
765    //         | Self::V0(StoreRepoV0::ProtectedStore(id))
766    //         | Self::V0(StoreRepoV0::Group(id))
767    //         | Self::V0(StoreRepoV0::PrivateStore(id)) => self.overlay_id_for_read_purpose(),
768    //         Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::inner(
769    //             &d.0,
770    //             store_overlay_branch_readcap_secret
771    //                 .expect("Dialog needs store_overlay_branch_readcap_secret"),
772    //         ),
773    //     }
774    // }
775
776    pub fn overlay_id_for_storage_purpose(&self) -> OverlayId {
777        match self {
778            Self::V0(StoreRepoV0::PublicStore(_id))
779            | Self::V0(StoreRepoV0::ProtectedStore(_id))
780            | Self::V0(StoreRepoV0::Group(_id))
781            | Self::V0(StoreRepoV0::PrivateStore(_id)) => self.overlay_id_for_read_purpose(),
782            Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone()),
783        }
784    }
785
786    pub fn overlay_id_for_write_purpose(
787        &self,
788        store_overlay_branch_readcap_secret: &ReadCapSecret,
789    ) -> OverlayId {
790        match self {
791            Self::V0(StoreRepoV0::PublicStore(id))
792            | Self::V0(StoreRepoV0::ProtectedStore(id))
793            | Self::V0(StoreRepoV0::Group(id))
794            | Self::V0(StoreRepoV0::PrivateStore(id)) => {
795                OverlayId::inner(id, store_overlay_branch_readcap_secret)
796            }
797            Self::V0(StoreRepoV0::Dialog(d)) => OverlayId::Inner(d.1.clone()),
798        }
799    }
800}
801
802/// Site type
803#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
804pub enum SiteType {
805    Org,
806    Individual((PrivKey, ReadCap)), // the priv_key of the user, and the read_cap of the private store
807}
808
809/// Site Store
810#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
811pub struct SiteStore {
812    pub id: PubKey,
813
814    pub store_type: SiteStoreType,
815}
816
817/// Site Store type
818#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
819pub enum SiteStoreType {
820    Public,
821    Protected,
822    Private,
823}
824
825/// Site Name
826#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
827pub enum SiteName {
828    Personal,
829    Name(String),
830}
831
832/// Reduced Site (for QRcode)
833#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
834pub struct ReducedSiteV0 {
835    pub user_key: PrivKey,
836
837    pub private_store_read_cap: ReadCap,
838
839    pub core: PubKey,
840    pub bootstraps: Vec<PubKey>,
841}
842
843//
844// BLOCKS common types
845//
846
847/// Internal node of a Merkle tree
848pub type InternalNode = Vec<BlockKey>;
849
850/// encrypted_content of BlockContentV0: a Merkle tree node
851#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
852pub enum ChunkContentV0 {
853    /// Internal node with references to children
854    InternalNode(InternalNode),
855
856    #[serde(with = "serde_bytes")]
857    DataChunk(Vec<u8>),
858}
859
860/// Header of a Commit, can be embedded or as a ref
861#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
862pub struct CommitHeaderV0 {
863    /// optional Commit Header ID
864    #[serde(skip)]
865    pub id: Option<ObjectId>,
866
867    /// Other objects this commit strongly depends on (ex: ADD for a REMOVE, files for an nfiles)
868    pub deps: Vec<ObjectId>,
869
870    /// dependency that is removed after this commit. used for reverts
871    pub ndeps: Vec<ObjectId>,
872
873    /// tells brokers that this is a hard snapshot and that all the ACKs and full causal past should be treated as ndeps (their body removed)
874    /// brokers will only perform the deletion of bodies after this commit has been ACKed by at least one subsequent commit
875    /// but if the next commit is a nack, the deletion is prevented.
876    pub compact: bool,
877
878    /// current valid commits in head
879    pub acks: Vec<ObjectId>,
880
881    /// head commits that are invalid
882    pub nacks: Vec<ObjectId>,
883
884    /// list of Files that are referenced in this commit
885    pub files: Vec<ObjectId>,
886
887    /// list of Files that are not referenced anymore after this commit
888    /// the commit(s) that created the files should be in deps
889    pub nfiles: Vec<ObjectId>,
890}
891
892#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
893pub enum CommitHeader {
894    V0(CommitHeaderV0),
895}
896
897/// Keys for the corresponding IDs contained in the Header
898#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
899pub struct CommitHeaderKeysV0 {
900    /// Other objects this commit strongly depends on (ex: ADD for a REMOVE, files for an nfiles)
901    pub deps: Vec<ObjectKey>,
902
903    // ndeps keys are not included because we don't need the keys to access the commits we will not need anymore
904    // the keys are in the deps of their respective subsequent commits in the DAG anyway
905    /// current valid commits in head
906    pub acks: Vec<ObjectKey>,
907
908    /// head commits that are invalid
909    pub nacks: Vec<ObjectKey>,
910
911    /// list of Files that are referenced in this commit. Exceptionally this is an ObjectRef, because
912    /// even if the CommitHeader is omitted, we want the Files to be openable.
913    pub files: Vec<ObjectRef>,
914    // nfiles keys are not included because we don't need the keys to access the files we will not need anymore
915    // the keys are in the deps of the respective commits that added them anyway
916}
917
918#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
919pub enum CommitHeaderKeys {
920    V0(CommitHeaderKeysV0),
921}
922
923#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
924pub enum CommitHeaderObject {
925    Id(ObjectId),
926    EncryptedContent(Vec<u8>),
927    None,
928    RandomAccess,
929}
930
931#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
932pub struct CommitHeaderRef {
933    pub obj: CommitHeaderObject,
934    pub key: ObjectKey,
935}
936
937impl CommitHeaderRef {
938    pub fn from_id_key(id: BlockId, key: ObjectKey) -> Self {
939        CommitHeaderRef {
940            obj: CommitHeaderObject::Id(id),
941            key,
942        }
943    }
944    pub fn from_content_key(content: Vec<u8>, key: ObjectKey) -> Self {
945        CommitHeaderRef {
946            obj: CommitHeaderObject::EncryptedContent(content),
947            key,
948        }
949    }
950    pub fn encrypted_content_len(&self) -> usize {
951        match &self.obj {
952            CommitHeaderObject::EncryptedContent(ec) => ec.len(),
953            _ => 0,
954        }
955    }
956}
957
958/// unencrypted part of the Block
959#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
960pub struct BlockContentV0 {
961    /// Reference (actually, only its ID or an embedded block if the size is small enough)
962    /// to a CommitHeader of the root Block of a commit that contains references to other objects (e.g. Commit deps & acks)
963    /// Only set if the block is a commit (and it is the root block of the Object).
964    /// It is an easy way to know if the Block is a commit (but be careful because some root commits can be without a header).
965    pub commit_header: CommitHeaderObject,
966
967    /// Block IDs for child nodes in the Merkle tree,
968    /// is empty if ObjectContent fits in one block or this block is a leaf. in both cases, encrypted_content is then not empty
969    pub children: Vec<BlockId>,
970
971    /// contains encrypted ChunkContentV0 (entirety, when fitting, or chunks of ObjectContentV0, in DataChunk) used for leaves of the Merkle tree,
972    /// or to store the keys of children (in InternalNode)
973    ///
974    /// Encrypted using convergent encryption with ChaCha20:
975    /// - convergence_key: BLAKE3 derive_key ("NextGraph Data BLAKE3 key",
976    ///                                        StoreRepo + store's repo ReadCapSecret )
977    ///                                     // basically similar to the InnerOverlayId but not hashed, so that brokers cannot do "confirmation of a file" attack
978    /// - key: BLAKE3 keyed hash (convergence_key, plain_chunk_content)
979    /// - nonce: 0
980    #[serde(with = "serde_bytes")]
981    pub encrypted_content: Vec<u8>,
982}
983
984/// Immutable object with encrypted content
985#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
986pub enum BlockContent {
987    V0(BlockContentV0),
988}
989
990impl BlockContent {
991    pub fn commit_header_obj(&self) -> &CommitHeaderObject {
992        match self {
993            Self::V0(v0) => &v0.commit_header,
994        }
995    }
996}
997
998/// Immutable block with encrypted content
999///
1000/// `ObjectContent` is chunked and stored as `Block`s in a Merkle tree.
1001/// A Block is a Merkle tree node.
1002#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1003pub struct BlockV0 {
1004    /// Block ID
1005    #[serde(skip)]
1006    pub id: Option<BlockId>,
1007
1008    /// Block Key
1009    #[serde(skip)]
1010    pub key: Option<SymKey>,
1011
1012    /// Header
1013    // #[serde(skip)]
1014    // TODO
1015    // pub header: Option<CommitHeader>,
1016
1017    /// Key needed to open the CommitHeader. can be omitted if the Commit is shared without its ancestors,
1018    /// or if the block is not a root block of commit, or that commit is a root commit (first in branch)
1019    pub commit_header_key: Option<ObjectKey>,
1020
1021    pub content: BlockContent,
1022}
1023
1024/// Immutable block with encrypted content
1025#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1026pub enum Block {
1027    V0(BlockV0),
1028}
1029
1030//
1031// REPO IMPLEMENTATION
1032//
1033
1034/// Repository definition
1035///
1036/// First commit published in root branch, signed by repository key
1037/// For the Root repo of a store(overlay), the convergence_key should be derived from :
1038/// "NextGraph Data BLAKE3 key", RepoId + RepoWriteCapSecret)
1039/// for a private store root repo, the RepoWriteCapSecret can be omitted
1040#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1041pub struct RepositoryV0 {
1042    /// Repo public key ID
1043    pub id: RepoId,
1044
1045    /// Verification program (WASM)
1046    #[serde(with = "serde_bytes")]
1047    pub verification_program: Vec<u8>,
1048
1049    /// Optional serialization of a ReadBranchLink (of a rootbranch or a transactional branch), if the repository is a fork of another one.
1050    /// then transaction branches of this new repo, will be able to reference the forked repo/branches commits as DEPS in their singleton Branch commit.
1051    #[serde(with = "serde_bytes")]
1052    pub fork_of: Vec<u8>,
1053
1054    /// User ID who created this repo
1055    pub creator: Option<UserId>,
1056
1057    // TODO: for org store root repo, should have a sig by the org priv_key, over the repoid, and a sig by this repo_priv_key over the org_id (to establish the bidirectional linking between org and store)
1058
1059    // TODO: discrete doc type
1060    // TODO: order (store, partial order, partial sign all commits,(conflict resolution strategy), total order, fsm, smart contract )
1061    // TODO: immutable conditions (allow_change_owners, allow_change_quorum, min_quorum, allow_inherit_perms, signers_can_be_editors, all_editors_are_signers, etc...)
1062    /// Immutable App-specific metadata
1063    #[serde(with = "serde_bytes")]
1064    pub metadata: Vec<u8>,
1065}
1066
1067/// Repository definition
1068#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1069pub enum Repository {
1070    V0(RepositoryV0),
1071}
1072
1073impl fmt::Display for Repository {
1074    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1075        match self {
1076            Self::V0(v0) => {
1077                writeln!(f, "V0")?;
1078                writeln!(f, "repo_id: {}", v0.id)?;
1079                writeln!(
1080                    f,
1081                    "creator: {}",
1082                    v0.creator.map_or("None".to_string(), |c| format!("{}", c))
1083                )?;
1084                Ok(())
1085            }
1086        }
1087    }
1088}
1089
1090/// Root Branch definition V0
1091///
1092/// Second commit in the root branch, signed by repository key
1093/// is used also to update the root branch definition when users are removed, quorum(s) are changed, repo is moved to other store.
1094/// In this case, it is signed by its author, and requires an additional group signature by the total_order_quorum or by the owners_quorum.
1095/// DEPS: Reference to the previous root branch definition commit, if it is an update
1096#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1097pub struct RootBranchV0 {
1098    /// Branch public key ID, equal to the repo_id
1099    pub id: PubKey,
1100
1101    /// Reference to the repository commit, to get the verification_program and other immutable details
1102    pub repo: ObjectRef,
1103
1104    /// Store ID the repo belongs to
1105    /// the identity is checked by verifiers (check overlay is matching)
1106    pub store: StoreOverlay,
1107
1108    /// signature of repoId with store's partial_order signature
1109    /// in order to verify that the store recognizes this repo as part of itself.
1110    /// only if not a store root repo itself
1111    pub store_sig: Option<Signature>,
1112
1113    /// Pub/sub topic ID for publishing events about the root branch
1114    pub topic: TopicId,
1115
1116    /// topic private key (a BranchWriteCapSecret), encrypted with a key derived as follow
1117    /// BLAKE3 derive_key ("NextGraph Branch WriteCap Secret BLAKE3 key",
1118    ///                                        RepoWriteCapSecret, TopicId, BranchId )
1119    /// so that only editors of the repo can decrypt the privkey
1120    /// nonce = 0
1121    /// not encrypted for individual store repo.
1122    #[serde(with = "serde_bytes")]
1123    pub topic_privkey: Vec<u8>,
1124
1125    /// if set, permissions are inherited from Store Repo.
1126    /// Optional is a store_read_cap
1127    /// (only set if this repo is not the store repo itself)
1128    /// check that it matches the self.store
1129    /// can only be committed by an owner
1130    /// it generates a new certificate
1131    /// owners are not inherited from store
1132    // TODO: ReadCap or PermaCap. If it is a ReadCap, a new RootBranch commit should be published (RootCapRefresh, only read_cap changes) every time the store read cap changes.
1133    /// empty for private repos, eventhough they are all implicitly inheriting perms from private store
1134    pub inherit_perms_users_and_quorum_from_store: Option<ReadCap>,
1135
1136    /// Quorum definition ObjectRef
1137    /// TODO: ObjectKey should be encrypted with SIGNER_KEY ?
1138    pub quorum: Option<ObjectRef>,
1139
1140    /// BEC periodic reconciliation interval. zero deactivates it
1141    pub reconciliation_interval: RelTime,
1142
1143    // list of owners. all of them are required to sign any RootBranch that modifies the list of owners or the inherit_perms_users_and_quorum_from_store field.
1144    pub owners: Vec<UserId>,
1145
1146    /// when the list of owners is changed, a crypto_box containing the RepoWriteCapSecret should be included here for each owner.
1147    /// this should also be done at creation time, with the UserId of the first owner, except for individual private store repo, because it doesnt have a RepoWriteCapSecret
1148    /// the vector has the same order and size as the owners one. each owner finds their write_cap here.
1149    pub owners_write_cap: Vec<serde_bytes::ByteBuf>,
1150
1151    /// Mutable App-specific metadata
1152    #[serde(with = "serde_bytes")]
1153    pub metadata: Vec<u8>,
1154}
1155
1156/// RootBranch definition
1157#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1158pub enum RootBranch {
1159    V0(RootBranchV0),
1160}
1161
1162impl RootBranch {
1163    pub fn topic(&self) -> &TopicId {
1164        match self {
1165            Self::V0(v0) => &v0.topic,
1166        }
1167    }
1168    pub fn owners(&self) -> &Vec<UserId> {
1169        match self {
1170            Self::V0(v0) => &v0.owners,
1171        }
1172    }
1173    pub fn encrypt_write_cap(
1174        for_user: &UserId,
1175        write_cap: &RepoWriteCapSecret,
1176    ) -> Result<Vec<u8>, NgError> {
1177        let ser = serde_bare::to_vec(write_cap).unwrap();
1178        let mut rng = crypto_box::aead::OsRng {};
1179        let cipher = crypto_box::seal(&mut rng, &for_user.to_dh_slice().into(), &ser)
1180            .map_err(|_| NgError::EncryptionError)?;
1181        Ok(cipher)
1182    }
1183    pub fn decrypt_write_cap(
1184        by_user: &PrivKey,
1185        cipher: &Vec<u8>,
1186    ) -> Result<RepoWriteCapSecret, NgError> {
1187        let ser = crypto_box::seal_open(&(*by_user.to_dh().slice()).into(), cipher)
1188            .map_err(|_| NgError::DecryptionError)?;
1189        let write_cap: RepoWriteCapSecret =
1190            serde_bare::from_slice(&ser).map_err(|_| NgError::SerializationError)?;
1191        Ok(write_cap)
1192    }
1193}
1194
1195impl fmt::Display for RootBranch {
1196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1197        match self {
1198            Self::V0(v0) => {
1199                writeln!(f, "V0")?;
1200                writeln!(f, "repo_id:   {}", v0.id)?;
1201                writeln!(f, "repo_ref:  {}", v0.repo)?;
1202                write!(f, "store:     {}", v0.store)?;
1203                writeln!(
1204                    f,
1205                    "store_sig: {}",
1206                    v0.store_sig
1207                        .as_ref()
1208                        .map_or("None".to_string(), |c| format!("{}", c))
1209                )?;
1210                writeln!(f, "topic:     {}", v0.topic)?;
1211                writeln!(
1212                    f,
1213                    "inherit_perms: {}",
1214                    v0.inherit_perms_users_and_quorum_from_store
1215                        .as_ref()
1216                        .map_or("None".to_string(), |c| format!("{}", c))
1217                )?;
1218                writeln!(
1219                    f,
1220                    "quorum: {}",
1221                    v0.quorum
1222                        .as_ref()
1223                        .map_or("None".to_string(), |c| format!("{}", c))
1224                )?;
1225                writeln!(f, "reconciliation_interval: {}", v0.reconciliation_interval)?;
1226                Ok(())
1227            }
1228        }
1229    }
1230}
1231
1232/// Quorum definition V0
1233///
1234/// Changed when the signers need to be updated. Signers are not necessarily editors of the repo, and they do not need to be members either, as they will be notified of RootCapRefresh anyway.
1235#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1236pub struct QuorumV0 {
1237    /// Number of signatures required for a partial order commit to be valid (threshold+1)
1238    pub partial_order_quorum: u32,
1239
1240    /// List of the users who can sign for partial order
1241    pub partial_order_users: Vec<UserId>,
1242
1243    /// Number of signatures required for a total order commit to be valid (threshold+1)
1244    pub total_order_quorum: u32,
1245
1246    /// List of the users who can sign for total order
1247    pub total_order_users: Vec<UserId>,
1248
1249    // TODO:
1250    // epoch: ObjectId pointing to rootbranch commit (read_cap_id)
1251    /// cryptographic material for Threshold signature
1252    #[serde(with = "serde_bytes")]
1253    pub metadata: Vec<u8>,
1254}
1255
1256/// Quorum definition, is part of the RootBranch commit
1257// TODO: can it be sent in the root branch without being part of a RootBranch ?
1258#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1259pub enum Quorum {
1260    V0(QuorumV0),
1261}
1262
1263#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1264pub enum BranchContentType {
1265    GraphOnly,
1266    YMap,
1267    YXml,
1268    YText,
1269    Automerge,
1270    //Rdfs,
1271    //Owl,
1272    //Shacl,
1273    //Shex,
1274    None, // this is used by Store, Overlay and User BranchTypes
1275          //Chat,
1276}
1277
1278/// Branch definition
1279///
1280/// First commit in a branch, signed by branch key
1281/// In case of a fork, the commit DEPS indicate
1282/// the previous branch heads, and the ACKS are empty.
1283///
1284/// Can be used also to update the branch definition when users are removed
1285/// In this case, the total_order quorum is needed, and DEPS indicates the BranchCapRefresh commit
1286#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1287pub struct BranchV0 {
1288    /// Branch public key ID
1289    pub id: PubKey,
1290
1291    pub content_type: BranchContentType,
1292
1293    /// Reference to the repository commit
1294    pub repo: ObjectRef,
1295
1296    /// object ID of the current root_branch commit (ReadCap), in order to keep in sync this branch with root_branch
1297    /// The key is not provided as external readers should not be able to access the root branch definition.
1298    /// it is only used by verifiers (who have the key already)
1299    pub root_branch_readcap_id: ObjectId,
1300
1301    /// Pub/sub topic for publishing events
1302    pub topic: PubKey,
1303
1304    /// topic private key (a BranchWriteCapSecret), encrypted with a key derived as follow
1305    /// BLAKE3 derive_key ("NextGraph Branch WriteCap Secret BLAKE3 key",
1306    ///                                        RepoWriteCapSecret, TopicId, BranchId )
1307    /// so that only editors of the repo can decrypt the privkey
1308    /// For individual store repo, the RepoWriteCapSecret is zero
1309    #[serde(with = "serde_bytes")]
1310    pub topic_privkey: Vec<u8>,
1311
1312    /// optional: this branch is the result of a pull request coming from another repo.
1313    /// contains a serialization of a ReadBranchLink of a transactional branch from another repo
1314    #[serde(with = "serde_bytes")]
1315    pub pulled_from: Vec<u8>,
1316
1317    /// App-specific metadata
1318    #[serde(with = "serde_bytes")]
1319    pub metadata: Vec<u8>,
1320}
1321
1322impl fmt::Display for Branch {
1323    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1324        match self {
1325            Self::V0(v0) => {
1326                writeln!(f, "V0")?;
1327                writeln!(f, "id:                     {}", v0.id)?;
1328                writeln!(f, "repo:                   {}", v0.repo)?;
1329                writeln!(f, "root_branch_readcap_id: {}", v0.root_branch_readcap_id)?;
1330                writeln!(f, "topic:                  {}", v0.topic)?;
1331                writeln!(f, "topic_privkey:          {:?}", v0.topic_privkey)?;
1332                Ok(())
1333            }
1334        }
1335    }
1336}
1337
1338/// Branch definition
1339#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1340pub enum Branch {
1341    V0(BranchV0),
1342}
1343
1344#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1345pub enum BranchType {
1346    Main, // Main is also transactional
1347    Chat,
1348    Store,
1349    Overlay,
1350    User,
1351    Transactional, // this could have been called OtherTransactional, but for the sake of simplicity, we use Transactional for any branch that is not the Main one.
1352    Root,          // only used for BranchInfo
1353                   //Unknown, // only used temporarily when loading a branch info from commits (Branch commit, then AddBranch commit)
1354}
1355
1356impl fmt::Display for BranchType {
1357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1358        write!(
1359            f,
1360            "{}",
1361            match self {
1362                Self::Main => "Main",
1363                Self::Chat => "Chat",
1364                Self::Store => "Store",
1365                Self::Overlay => "Overlay",
1366                Self::User => "User",
1367                Self::Transactional => "Transactional",
1368                Self::Root => "Root",
1369                //Self::Unknown => "==unknown==",
1370            }
1371        )
1372    }
1373}
1374
1375/// Add a branch to the repository
1376///
1377/// DEPS: if update branch: previous AddBranch commit of the same branchId
1378#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1379pub struct AddBranchV0 {
1380    /// the new topic_id (will be needed immediately by future readers
1381    /// in order to subscribe to the pub/sub). should be identical to the one in the Branch definition
1382    pub topic_id: TopicId,
1383
1384    pub branch_id: BranchId,
1385
1386    pub branch_type: BranchType,
1387
1388    // the new branch definition commit
1389    // (we need the ObjectKey in order to open the pub/sub Event)
1390    pub branch_read_cap: ReadCap,
1391}
1392
1393impl fmt::Display for AddBranch {
1394    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1395        match self {
1396            Self::V0(v0) => {
1397                writeln!(f, "V0         {}", v0.branch_type)?;
1398                writeln!(f, "topic_id:         {}", v0.topic_id)?;
1399                writeln!(f, "branch_read_cap:  {}", v0.branch_read_cap)?;
1400                Ok(())
1401            }
1402        }
1403    }
1404}
1405
1406/// Add a branch to the repository
1407#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1408pub enum AddBranch {
1409    V0(AddBranchV0),
1410}
1411
1412pub type RemoveBranchV0 = ();
1413
1414/// Remove a branch from the repository
1415///
1416/// DEPS: should point to the previous AddBranch.
1417#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1418pub enum RemoveBranch {
1419    V0(RemoveBranchV0),
1420}
1421
1422/// Add member to a repo
1423#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1424pub struct AddMemberV0 {
1425    /// Member to add
1426    pub member: UserId,
1427
1428    /// App-specific metadata
1429    /// (role, app level permissions, cryptographic material, etc)
1430    #[serde(with = "serde_bytes")]
1431    pub metadata: Vec<u8>,
1432}
1433
1434#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1435pub enum AddMember {
1436    V0(AddMemberV0),
1437}
1438
1439/// Remove member from a repo
1440///
1441/// An owner cannot be removed (it cannot be added even)
1442/// The overlay should be refreshed if user was malicious, after the user is removed from last repo. See REFRESH_READ_CAP on store repo.
1443#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1444pub struct RemoveMemberV0 {
1445    /// Member to remove
1446    pub member: UserId,
1447
1448    /// should this user be banned and prevented from being invited again by anybody else
1449    pub banned: bool,
1450
1451    /// Metadata
1452    /// (reason, etc...)
1453    #[serde(with = "serde_bytes")]
1454    pub metadata: Vec<u8>,
1455}
1456
1457#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1458pub enum RemoveMember {
1459    V0(RemoveMemberV0),
1460}
1461
1462/// when a signing capability is removed, a new SignerSecretKeys should be added to wallet, with the removed key set to None
1463#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1464pub struct SignerCap {
1465    pub repo: RepoId,
1466
1467    /// latest RootBranch commit or Quorum commit that defines the signing epoch
1468    pub epoch: ObjectId,
1469
1470    pub owner: Option<SerdeSecret<threshold_crypto::SecretKeyShare>>,
1471
1472    pub total_order: Option<SerdeSecret<threshold_crypto::SecretKeyShare>>,
1473
1474    pub partial_order: Option<SerdeSecret<threshold_crypto::SecretKeyShare>>,
1475}
1476
1477/// Permissions
1478#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1479pub enum PermissionV0 {
1480    Create, // Used internally by the creator at creation time. Not part of the permission set that can be added and removed
1481    Owner,  // used internally for owners
1482
1483    //
1484    // permissions delegated by owners and admins (all admins inherit them)
1485    //
1486    AddReadMember, // adds a member to the repo (AddMember). without additional perm, the user is a reader
1487    RemoveMember, // if user has any specific perm, RemoveWritePermission, RefreshWriteCap and/or Admin permission is needed. always behind SyncSignature
1488    AddWritePermission, // can send AddPermission that add 3 perms to other user: WriteAsync, WriteSync, and RefreshWriteCap
1489    WriteAsync, // can send AsyncTransaction, AddFile, RemoveFile, Snapshot, optionally with AsyncSignature
1490    WriteSync,  // can send SyncTransaction, AddFile, RemoveFile, always behind SyncSignature
1491    Compact,    // can send Compact, always behind SyncSignature
1492    RemoveWritePermission, // can send RemovePermission that remove the WriteAsync, WriteSync or RefreshWriteCap permissions from user. RefreshWriteCap will probably be needed by the user who does the RemovePermission
1493
1494    AddBranch,    // can send AddBranch and Branch commits
1495    RemoveBranch, // can send removeBranch, always behind SyncSignature
1496    ChangeName,   // can send AddName and RemoveName
1497
1498    RefreshReadCap, // can send RootCapRefresh or BranchCapRefresh that do not contain a write_cap, followed by UpdateRootBranch and/or UpdateBranch commits, with or without renewed topicIds. Always behind SyncSignature
1499    RefreshWriteCap, // can send RootCapRefresh that contains a write_cap and associated BranchCapRefreshes, followed by UpdateRootBranch and associated UpdateBranch commits on all branches, with renewed topicIds and RepoWriteCapSecret. Always behind SyncSignature
1500
1501    //
1502    // permissions delegated by owners:
1503    //
1504    ChangeQuorum, // can add and remove Signers, change the quorum thresholds for total order and partial order. implies the RefreshReadCap perm (without changing topicids). Always behind SyncSignature
1505    Admin, // can administer the repo: assigns perms to other user with AddPermission and RemovePermission. RemovePermission always behind SyncSignature
1506    ChangeMainBranch,
1507
1508    // other permissions. TODO: specify them more in details
1509    Chat,           // can chat
1510    Inbox,          // can read inbox
1511    PermaShare,     // can create and answer to PermaCap (PermaLink)
1512    UpdateStore,    // only for store root repo (add repo, remove repo) to the store special branch
1513    RefreshOverlay, // Equivalent to BranchCapRefresh for the overlay special branch.
1514}
1515
1516/// Add permission to a member in a repo
1517#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1518pub struct AddPermissionV0 {
1519    /// Member receiving the permission
1520    pub member: UserId,
1521
1522    /// Permission given to user
1523    pub permission: PermissionV0,
1524
1525    /// Metadata
1526    /// (role, app level permissions, cryptographic material, etc)
1527    /// if the added permission is a write one, a crypto_box containing the RepoWriteCapSecret should be included here for the member that receives the perm.
1528    ///
1529    /// Can be some COMMON KEY privkey encrypted with the user pubkey
1530    /// If a PROOF for the common key is needed, should be sent here too
1531    /// COMMON KEYS are: SHARE, INBOX,
1532    #[serde(with = "serde_bytes")]
1533    pub metadata: Vec<u8>,
1534}
1535
1536#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1537pub enum AddPermission {
1538    V0(AddPermissionV0),
1539}
1540
1541impl AddPermission {
1542    pub fn permission_v0(&self) -> &PermissionV0 {
1543        match self {
1544            Self::V0(v0) => &v0.permission,
1545        }
1546    }
1547}
1548
1549/// Remove permission from a user in a repo
1550#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1551pub struct RemovePermissionV0 {
1552    /// Member to remove
1553    pub member: UserId,
1554
1555    /// Permission removed from user
1556    pub permission: PermissionV0,
1557
1558    /// Metadata
1559    /// (reason, new cryptographic materials...)
1560    /// If the permission was linked to a COMMON KEY, a new privkey should be generated
1561    /// and sent to all users that still have this permission, encrypted with their respective pubkey
1562    /// If a PROOF for the common key is needed, should be sent here too
1563    #[serde(with = "serde_bytes")]
1564    pub metadata: Vec<u8>,
1565}
1566
1567#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1568pub enum RemovePermission {
1569    V0(RemovePermissionV0),
1570}
1571
1572impl RemovePermission {
1573    pub fn permission_v0(&self) -> &PermissionV0 {
1574        match self {
1575            Self::V0(v0) => &v0.permission,
1576        }
1577    }
1578}
1579
1580#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1581pub enum RepoNamedItemV0 {
1582    Branch(BranchId),
1583    Commit(ObjectId),
1584}
1585
1586#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1587pub enum RepoNamedItem {
1588    V0(RepoNamedItemV0),
1589}
1590
1591/// Add a new name in the repo that can point to a branch or a commit
1592///
1593/// Or change the value of a name
1594/// DEPS: if it is a change of value: all the previous AddName commits seen for this name
1595#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1596pub struct AddNameV0 {
1597    /// the name. in case of conflict, the smallest Id is taken.
1598    pub name: String,
1599
1600    /// A branch, commit or file
1601    pub item: RepoNamedItem,
1602
1603    /// Metadata
1604    #[serde(with = "serde_bytes")]
1605    pub metadata: Vec<u8>,
1606}
1607
1608#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1609pub enum AddName {
1610    V0(AddNameV0),
1611}
1612
1613/// Remove a name from the repo, using ORset CRDT logic
1614///
1615/// DEPS: all the AddName commits seen for this name
1616#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1617pub struct RemoveNameV0 {
1618    /// name to remove
1619    pub name: String,
1620
1621    /// Metadata
1622    #[serde(with = "serde_bytes")]
1623    pub metadata: Vec<u8>,
1624}
1625
1626#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1627pub enum RemoveName {
1628    V0(RemoveNameV0),
1629}
1630
1631//
1632// Commits on Store branch
1633//
1634
1635/// Adds a repo into the store branch.
1636///
1637/// The repo's `store` field should match the store
1638/// DEPS to the previous AddRepo commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match
1639#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1640pub struct AddRepoV0 {
1641    pub read_cap: ReadCap,
1642
1643    /// Metadata
1644    #[serde(with = "serde_bytes")]
1645    pub metadata: Vec<u8>,
1646}
1647
1648#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1649pub enum AddRepo {
1650    V0(AddRepoV0),
1651}
1652
1653/// Removes a repo from the store branch.
1654///
1655/// DEPS to the previous AddRepo commit(s) (ORset logic) with matching repo_id
1656#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1657pub struct RemoveRepoV0 {
1658    pub id: RepoId,
1659
1660    /// Metadata
1661    #[serde(with = "serde_bytes")]
1662    pub metadata: Vec<u8>,
1663}
1664
1665#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1666pub enum RemoveRepo {
1667    V0(RemoveRepoV0),
1668}
1669
1670// TODO: publish (for public site only)
1671
1672//
1673// Commits on User branch
1674//
1675
1676/// Adds a link into the user branch, so that a user can share with all its device a new Link they received.
1677///
1678/// The repo's `store` field should not match with any store of the user. Only external repos are accepted here.
1679/// DEPS to the previous AddLink commit(s) if it is an update. in this case, repo_id of the referenced rootbranch definition(s) should match
1680#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1681pub struct AddLinkV0 {
1682    pub read_cap: ReadCap,
1683
1684    /// Metadata
1685    #[serde(with = "serde_bytes")]
1686    pub metadata: Vec<u8>,
1687}
1688
1689#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1690pub enum AddLink {
1691    V0(AddLinkV0),
1692}
1693
1694/// Removes a link from the `user` branch.
1695///
1696/// DEPS to the previous AddLink commit(s) (ORset logic) with matching repo_id
1697#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1698pub struct RemoveLinkV0 {
1699    pub id: RepoId,
1700
1701    /// Metadata
1702    #[serde(with = "serde_bytes")]
1703    pub metadata: Vec<u8>,
1704}
1705
1706#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1707pub enum RemoveLink {
1708    V0(RemoveLinkV0),
1709}
1710
1711/// Adds a SignerCap into the user branch,
1712///
1713/// so that a user can share with all its device a new signing capability that was just created.
1714/// The cap's `epoch` field should be dereferenced and the user must be part of the quorum/owners.
1715/// DEPS to the previous AddSignerCap commit(s) if it is an update. in this case, repo_ids have to match,
1716/// and the the referenced rootbranch definition(s) should have compatible causal past (the newer AddSignerCap must have a newer epoch compared to the one of the replaced cap )
1717#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1718pub struct AddSignerCapV0 {
1719    pub cap: SignerCap,
1720
1721    /// Metadata
1722    #[serde(with = "serde_bytes")]
1723    pub metadata: Vec<u8>,
1724}
1725
1726#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1727pub enum AddSignerCap {
1728    V0(AddSignerCapV0),
1729}
1730
1731impl fmt::Display for AddSignerCap {
1732    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1733        match self {
1734            Self::V0(v0) => {
1735                writeln!(f, "V0")?;
1736                writeln!(f, "cap:   {:?}", v0.cap)?;
1737
1738                Ok(())
1739            }
1740        }
1741    }
1742}
1743
1744/// Removes a SignerCap from the `user` branch.
1745///
1746/// DEPS to the previous AddSignerCap commit(s) (ORset logic) with matching repo_id
1747#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1748pub struct RemoveSignerCapV0 {
1749    pub id: RepoId,
1750
1751    /// Metadata
1752    #[serde(with = "serde_bytes")]
1753    pub metadata: Vec<u8>,
1754}
1755
1756#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1757pub enum RemoveSignerCap {
1758    V0(RemoveSignerCapV0),
1759}
1760
1761/// Adds a wallet operation so all the devices can sync their locally saved wallet on disk (at the next wallet opening)
1762///
1763/// DEPS are the last HEAD of wallet updates.
1764#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1765pub struct WalletUpdateV0 {
1766    #[serde(with = "serde_bytes")]
1767    pub op: Vec<u8>,
1768
1769    /// Metadata
1770    #[serde(with = "serde_bytes")]
1771    pub metadata: Vec<u8>,
1772}
1773
1774#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1775pub enum WalletUpdate {
1776    V0(WalletUpdateV0),
1777}
1778
1779/// Updates the ReadCap of the public, protected sites, Group and Dialog stores of the User
1780///
1781/// DEPS to the previous ones.
1782/// this is used to speedup joining the overlay of such stores, for new devices on new brokers
1783/// so they don't have to read the whole pub/sub of the StoreRepo in order to get the last ReadCap
1784#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1785pub struct StoreUpdateV0 {
1786    // id of the store.
1787    pub store: StoreRepo,
1788
1789    pub store_read_cap: ReadCap,
1790
1791    pub overlay_branch_read_cap: ReadCap,
1792
1793    /// Metadata
1794    #[serde(with = "serde_bytes")]
1795    pub metadata: Vec<u8>,
1796}
1797
1798#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1799pub enum StoreUpdate {
1800    V0(StoreUpdateV0),
1801}
1802
1803impl fmt::Display for StoreUpdate {
1804    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1805        match self {
1806            Self::V0(v0) => {
1807                writeln!(f, "V0")?;
1808                writeln!(f, "store:   {}", v0.store)?;
1809                writeln!(f, "store_read_cap:  {}", v0.store_read_cap)?;
1810                write!(
1811                    f,
1812                    "overlay_branch_read_cap:     {}",
1813                    v0.overlay_branch_read_cap
1814                )?;
1815                Ok(())
1816            }
1817        }
1818    }
1819}
1820
1821//
1822//  Commits on transaction branches
1823//
1824
1825/// Transaction with CRDT operations
1826// TODO: edeps: List<(repo_id,ObjectRef)>
1827// TODO: rcpts: List<repo_id>
1828pub type TransactionV0 = Vec<u8>;
1829
1830#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1831pub enum Transaction {
1832    #[serde(with = "serde_bytes")]
1833    V0(TransactionV0),
1834}
1835
1836/// Add a new binary file in a branch
1837///
1838/// FILES: the file ObjectRef
1839#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1840pub struct AddFileV0 {
1841    /// an optional name. does not conflict (not unique across the branch nor repo)
1842    pub name: Option<String>,
1843
1844    /// Metadata
1845    #[serde(with = "serde_bytes")]
1846    pub metadata: Vec<u8>,
1847}
1848
1849#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1850pub enum AddFile {
1851    V0(AddFileV0),
1852}
1853
1854impl fmt::Display for AddFile {
1855    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1856        match self {
1857            Self::V0(v0) => {
1858                writeln!(f, "V0")?;
1859                writeln!(f, "name: {:?}", v0.name)
1860            }
1861        }
1862    }
1863}
1864
1865impl AddFile {
1866    pub fn name(&self) -> &Option<String> {
1867        match self {
1868            Self::V0(v0) => &v0.name,
1869        }
1870    }
1871}
1872
1873/// Remove a file from the branch, using ORset CRDT logic
1874///
1875/// (removes the ref counting. not necessarily the file itself)
1876/// NFILES: the file ObjectRef
1877/// DEPS: all the visible AddFile commits in the branch (ORset)
1878pub type RemoveFileV0 = ();
1879
1880#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1881pub enum RemoveFile {
1882    V0(RemoveFileV0),
1883}
1884
1885/// Snapshot of a Branch
1886///
1887/// Contains a data structure
1888/// computed from the commits at the specified head.
1889/// ACKS contains the head the snapshot was made from
1890#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1891pub struct SnapshotV0 {
1892    // Branch heads the snapshot was made from, can be useful when shared outside and the commit_header_key is set to None. otherwise it is redundant to ACKS
1893    pub heads: Vec<ObjectId>,
1894
1895    /// Snapshot data structure
1896    #[serde(with = "serde_bytes")]
1897    pub content: Vec<u8>,
1898}
1899
1900/// Snapshot of a Branch
1901#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1902pub enum Snapshot {
1903    V0(SnapshotV0),
1904}
1905
1906/// Compact: Hard Snapshot of a Branch
1907///
1908/// Contains a data structure
1909/// computed from the commits at the specified head.
1910/// ACKS contains the head the snapshot was made from
1911///
1912/// hard snapshot will erase all the CommitBody of ancestors in the branch
1913/// the compact boolean should be set in the Header too.
1914/// after a hard snapshot, it is recommended to refresh the read capability (to empty the topics of they keys they still hold)
1915/// If a branch is based on a hard snapshot, it cannot be merged back into the branch where the hard snapshot was made.
1916#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1917pub struct CompactV0 {
1918    // Branch heads the snapshot was made from, can be useful when shared outside and the commit_header_key is set to None. otherwise it is redundant to ACKS
1919    pub heads: Vec<ObjectId>,
1920
1921    // optional serialization of a ReadBranchLink, if the snapshot is made from another repo.
1922    #[serde(with = "serde_bytes")]
1923    pub origin: Vec<u8>,
1924
1925    /// Snapshot data structure
1926    #[serde(with = "serde_bytes")]
1927    pub content: Vec<u8>,
1928}
1929
1930/// Snapshot of a Branch
1931#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1932pub enum Compact {
1933    V0(CompactV0),
1934}
1935
1936// Async Threshold Signature of a commit V0 based on the partial order quorum
1937//
1938// Can sign Transaction, AddFile, and Snapshot, after they have been committed to the DAG.
1939// DEPS: the signed commits
1940// #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1941// pub struct AsyncSignatureV0 {
1942//     /// An Object containing the Threshold signature
1943//     pub signature: ObjectRef,
1944// }
1945
1946/// Async Threshold Signature of a commit based on the partial order quorum
1947#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1948pub enum AsyncSignature {
1949    V0(ObjectRef),
1950}
1951
1952impl AsyncSignature {
1953    pub fn verify(&self) -> bool {
1954        // check that the signature object referenced here, is of type threshold_sig Partial
1955        unimplemented!();
1956    }
1957}
1958
1959/// Sync Threshold Signature of one or a chain of commits . V0
1960///
1961/// points to the new Signature Object
1962/// based on the total order quorum (or owners quorum)
1963/// mandatory for UpdateRootBranch, UpdateBranch, some AddBranch, RemoveBranch, RemoveMember, RemovePermission, Quorum, Compact, sync Transaction, RootCapRefresh, BranchCapRefresh
1964/// DEPS: the last signed commit in chain
1965/// ACKS: previous head before the chain of signed commit(s). should be identical to the HEADS (marked as DEPS) of first commit in chain
1966// #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1967// pub struct SyncSignatureV0 {
1968//     /// An Object containing the Threshold signature
1969//     pub signature: ObjectRef,
1970// }
1971#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1972pub enum SyncSignature {
1973    V0(ObjectRef),
1974}
1975
1976impl SyncSignature {
1977    pub fn verify_quorum(&self) -> bool {
1978        // check that the signature object referenced here, is of type threshold_sig Total or Owner
1979        unimplemented!();
1980    }
1981}
1982
1983impl fmt::Display for SyncSignature {
1984    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1985        match self {
1986            Self::V0(v0) => {
1987                writeln!(f, "V0")?;
1988                writeln!(f, "{}", v0)?;
1989                Ok(())
1990            }
1991        }
1992    }
1993}
1994
1995/// the second tuple member is only set when a write_cap refresh is performed, and for users that are Editor (any Member that also has at least one permission, plus all the Owners)
1996#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
1997pub struct RefreshSecretV0(SymKey, Option<SymKey>);
1998
1999#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2000pub struct RefreshCapV0 {
2001    /// an ordered list of user IDs, with their corresponding crypto_box of a RefreshSecretV0.
2002    /// A hashed User ID for each Member (use author_digest()), Signer and Owner of the repo (except the one that is being excluded, if any)
2003    /// the ordering is important as it allows receivers to perform a binary search on the array (searching for their own ID)
2004    /// the refresh secret is used for encrypting the SyncSignature commit's key in the event sent in old topic (RefreshSecretV0.0) and for an optional write_cap refresh (RefreshSecretV0.1)
2005    pub refresh_secret: Vec<(Digest, serde_bytes::ByteBuf)>,
2006}
2007
2008/// RefreshCap
2009#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2010pub enum RefreshCap {
2011    V0(RefreshCapV0),
2012}
2013
2014/// RootCapRefresh. renew the capabilities of the root branch, or all transactional branches and the root_branch.
2015///
2016/// Each branch forms its separate chain for that purpose.
2017/// can refresh the topic ids, or not
2018/// ACKS: current HEADS in the branch at the moment of refresh. DEPS to the previous RootBranch commit that will be superseded.
2019/// the chain on the root_branch is : RootCapRefresh -> RemovePermission/RemoveMember -> UpdateRootBranch -> optional AddPermission(s) -> AddBranch x for each branch
2020/// and on each transactional branch: BranchCapRefresh -> UpdateBranch
2021/// always eventually followed at the end of each chain by a SyncSignature (each branch its own).
2022/// The key used in EventV0 to encrypt the key for that SyncSignature commit is the refresh_secret (RefreshSecretV0.0).
2023///
2024/// On each new topic, the first commit (singleton) is a BranchCapRefreshed that contains internal references to the old branch (but no DEPS or ACKS).
2025
2026#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2027pub struct RootCapRefreshV0 {
2028    // ObjectRef to the RefreshCap object
2029    pub refresh_ref: ObjectRef,
2030
2031    /// write cap encrypted with the refresh_secret RefreshSecretV0.1
2032    /// only allowed if the user has RefreshWriteCap permission
2033    pub write_cap: Option<RepoWriteCapSecret>,
2034}
2035
2036///
2037#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2038pub enum RootCapRefresh {
2039    V0(RootCapRefreshV0),
2040}
2041
2042/// BranchCapRefresh renew the capabilities of one specific transactional branch
2043///
2044/// ACKS: current HEADS in the branch at the moment of refresh.  DEPS to the previous Branch commit that will be superseded.
2045/// the chain is, on the transactional branch: BranchCapRefresh -> UpdateBranch
2046/// if this is an isolated branch refresh (not part of a rootcaprefresh), then the root branch chain is : AddBranch (ACKS to HEADS, quorumtype:TotalOrder )
2047/// always eventually followed at the end of each chain by a SyncSignature (each branch its own)
2048/// The key used in EventV0 to encrypt the key for that SyncSignature commit is the refresh_secret (RefreshSecretV0.0), but not on the root branch if it is an isolated branch refresh
2049///
2050/// On the new topic, the first commit (singleton) is a BranchCapRefreshed that contains internal references to the old branch (but no DEPS or ACKS).
2051
2052#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2053pub struct BranchCapRefreshV0 {
2054    /// ObjectRef to the RefreshCap object (shared with a root branch and other transac branches, or specially crafted for this branch if it is an isolated branch refresh)
2055    pub refresh_ref: ObjectRef,
2056}
2057
2058/// BranchCapRefresh
2059#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2060pub enum BranchCapRefresh {
2061    V0(BranchCapRefreshV0),
2062}
2063
2064/// BranchCapRefreshed is a singleton in a new topic. it has no ACKS nor DEPS.
2065#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2066pub struct BranchCapRefreshedV0 {
2067    /// reference to the previous read_cap of the branch
2068    pub continuation_of: ReadCap,
2069
2070    /// reference to the SyncSignature commit that did the refresh
2071    pub refresh: ObjectRef,
2072
2073    /// reference to the UpdateBranch/UpdateRootBranch commit within the event  of the SyncSignature
2074    pub new_read_cap: ReadCap,
2075}
2076
2077/// BranchCapRefreshed
2078#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2079pub enum BranchCapRefreshed {
2080    V0(BranchCapRefreshedV0),
2081}
2082
2083/// A Threshold Signature content
2084#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2085pub struct SignatureContentV0 {
2086    /// list of all the "end of chain" commit for each branch when doing a SyncSignature, or a list of arbitrary commits to sign, for AsyncSignature.
2087    pub commits: Vec<ObjectId>,
2088}
2089
2090/// A Signature content
2091#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2092pub enum SignatureContent {
2093    V0(SignatureContentV0),
2094}
2095
2096impl fmt::Display for SignatureContent {
2097    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2098        match self {
2099            Self::V0(v0) => {
2100                writeln!(f, "V0 == Commits: {}", v0.commits.len())?;
2101                let mut i = 0;
2102                for block_id in &v0.commits {
2103                    writeln!(f, "========== {:03}: {}", i, block_id)?;
2104                    i += 1;
2105                }
2106                Ok(())
2107            }
2108        }
2109    }
2110}
2111
2112/// A Threshold Signature and the set used to generate it
2113#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2114pub enum ThresholdSignatureV0 {
2115    PartialOrder(threshold_crypto::Signature),
2116    TotalOrder(threshold_crypto::Signature),
2117    Owners(threshold_crypto::Signature),
2118}
2119
2120impl fmt::Display for ThresholdSignatureV0 {
2121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2122        match self {
2123            Self::PartialOrder(_) => {
2124                writeln!(f, "PartialOrder")
2125            }
2126            Self::TotalOrder(_) => {
2127                writeln!(f, "TotalOrder")
2128            }
2129            Self::Owners(_) => {
2130                writeln!(f, "Owners")
2131            }
2132        }
2133    }
2134}
2135
2136/// A Threshold Signature object (not a commit) containing all the information that the signers have prepared.
2137#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2138pub struct SignatureV0 {
2139    /// the content that is signed
2140    pub content: SignatureContent,
2141
2142    /// The threshold signature itself. can come from 3 different sets
2143    pub threshold_sig: ThresholdSignatureV0,
2144
2145    /// A reference to the Certificate that should be used to verify this signature.
2146    pub certificate_ref: ObjectRef,
2147}
2148
2149impl fmt::Display for Signature {
2150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2151        match self {
2152            Self::V0(v0) => {
2153                writeln!(f, "V0")?;
2154                writeln!(f, "content:        {}", v0.content)?;
2155                writeln!(f, "threshold_sig:  {}", v0.threshold_sig)?;
2156                writeln!(f, "certificate_ref:{}", v0.certificate_ref)?;
2157                Ok(())
2158            }
2159        }
2160    }
2161}
2162
2163/// A Signature object (it is not a commit), referenced in AsyncSignature or SyncSignature
2164#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2165pub enum Signature {
2166    V0(SignatureV0),
2167}
2168
2169/// Enum for "orders" PKsets.
2170///
2171/// Can be inherited from the store, in this case, it is an ObjectRef pointing to the latest Certificate of the store.
2172/// Or can be 2 PublicKey defined specially for this repo,
2173/// .0 one for the total_order (first one). it is a PublicKeysSet so that verifier can see the threshold value, and can also verify Shares individually
2174/// .1 the other for the partial_order (second one. a PublicKey. is optional, as some repos are forcefully totally ordered and do not have this set).
2175#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2176pub enum OrdersPublicKeySetsV0 {
2177    Store(ObjectRef),
2178    Repo(
2179        (
2180            threshold_crypto::PublicKeySet,
2181            Option<threshold_crypto::PublicKey>,
2182        ),
2183    ),
2184    None, // the total_order quorum is not defined (yet, or anymore). there are no signers for the total_order, neither for the partial_order. The owners replace them.
2185}
2186
2187/// A Certificate content, that will be signed by the previous certificate signers.
2188#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2189pub struct CertificateContentV0 {
2190    /// the previous certificate in the chain of trust. Can be another Certificate or the Repository commit when we are at the root of the chain of trust.
2191    pub previous: ObjectRef,
2192
2193    /// The Commit Id of the latest RootBranch definition (= the ReadCap ID) in order to keep in sync with the options for signing.
2194    /// not used for verifying (this is why the secret is not present).
2195    pub readcap_id: ObjectId,
2196
2197    /// PublicKey used by the Owners. verifier uses this PK if the signature was issued by the Owners.
2198    pub owners_pk_set: threshold_crypto::PublicKey,
2199
2200    /// two "orders" PublicKeys (total_order and partial_order).
2201    pub orders_pk_sets: OrdersPublicKeySetsV0,
2202}
2203
2204/// A Signature of a Certificate, with an indication of which the threshold keyset or private key used to generate it
2205#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2206pub enum CertificateSignatureV0 {
2207    /// the root CertificateContentV0 is signed with the PrivKey of the Repo
2208    Repo(Sig),
2209    /// Any other certificate in the chain of trust is signed by the total_order quorum of the previous certificate, hence establishing the chain of trust.
2210    TotalOrder(threshold_crypto::Signature),
2211    /// if the previous cert's total order PKset has a threshold value of 0 or 1 (1 or 2 signers in the quorum),
2212    /// then it is allowed that the next certificate (this one) will be signed by the owners PKset instead.
2213    /// This is for a simple reason: if a user is removed from the list of signers in the total_order quorum,
2214    /// then in those 2 cases, the excluded signer will probably not cooperate to their exclusion, and will not sign the new certificate.
2215    /// to avoid deadlocks, we allow the owners to step in and sign the new cert instead.
2216    /// The Owners are also used when there is no quorum/signer defined (OrdersPublicKeySetsV0::None).
2217    Owners(threshold_crypto::Signature),
2218    /// in case the new certificate being signed is an update on the store certificate (OrdersPublicKeySetsV0::Store(ObjectRef) has changed from previous cert)
2219    /// then the signature is in that new store certificate, and not here. nothing else should have changed in the CertificateContent, and the validity of the new store cert has to be checked
2220    Store,
2221}
2222
2223/// A Certificate object (not a commit) containing all the information needed to verify a signature.
2224#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2225pub struct CertificateV0 {
2226    /// content of the certificate, which is signed here below by the previous certificate signers.
2227    pub content: CertificateContentV0,
2228
2229    /// signature over the content.
2230    pub sig: CertificateSignatureV0,
2231}
2232
2233/// A certificate object
2234#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2235pub enum Certificate {
2236    V0(CertificateV0),
2237}
2238
2239/// Commit body V0
2240#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2241pub enum CommitBodyV0 {
2242    //
2243    // for root branch:
2244    //
2245    Repository(Repository), // singleton and should be first in root_branch
2246    RootBranch(RootBranch), // singleton and should be second in root_branch
2247    UpdateRootBranch(RootBranch), // total order enforced with total_order_quorum
2248    RootCapRefresh(RootCapRefresh), // total order enforced with total_order_quorum
2249    AddMember(AddMember),   // total order enforced with total_order_quorum
2250    RemoveMember(RemoveMember), // total order enforced with total_order_quorum
2251    AddPermission(AddPermission),
2252    RemovePermission(RemovePermission),
2253    AddBranch(AddBranch),
2254    RemoveBranch(RemoveBranch),
2255    AddName(AddName),
2256    RemoveName(RemoveName),
2257    Delete(()), // signed with owners key. Deletes the repo
2258
2259    // TODO? Quorum(Quorum), // changes the quorum without changing the RootBranch
2260
2261    //
2262    // For transactional branches:
2263    //
2264    Branch(Branch),                     // singleton and should be first in branch
2265    BranchCapRefresh(BranchCapRefresh), // total order enforced with total_order_quorum
2266    UpdateBranch(Branch),               // total order enforced with total_order_quorum
2267    Snapshot(Snapshot),                 // a soft snapshot
2268    AsyncTransaction(Transaction),      // partial_order
2269    SyncTransaction(Transaction),       // total_order
2270    AddFile(AddFile),
2271    RemoveFile(RemoveFile),
2272    Compact(Compact), // a hard snapshot. total order enforced with total_order_quorum
2273    //Merge(Merge),
2274    //Revert(Revert), // only possible on partial order commit
2275    AsyncSignature(AsyncSignature),
2276
2277    //
2278    // For both
2279    //
2280    CapRefreshed(BranchCapRefreshed), // singleton and should be first in renewed branch
2281    SyncSignature(SyncSignature),
2282
2283    //
2284    // For store branch:
2285    //
2286    AddRepo(AddRepo),
2287    RemoveRepo(RemoveRepo),
2288
2289    //
2290    // For user branch:
2291    //
2292    AddLink(AddLink),
2293    RemoveLink(RemoveLink),
2294    AddSignerCap(AddSignerCap),
2295    RemoveSignerCap(RemoveSignerCap),
2296    WalletUpdate(WalletUpdate),
2297    StoreUpdate(StoreUpdate),
2298}
2299
2300/// Commit body
2301#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2302pub enum CommitBody {
2303    V0(CommitBodyV0),
2304}
2305
2306#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2307pub enum QuorumType {
2308    NoSigning,
2309    PartialOrder,
2310    TotalOrder,
2311    Owners,
2312    IamTheSignature,
2313}
2314
2315/// Content of a Commit
2316#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2317pub struct CommitContentV0 {
2318    /// Commit author (a hash of UserId)
2319    /// BLAKE3 keyed hash over UserId
2320    /// - key: BLAKE3 derive_key ("NextGraph UserId Hash Overlay Id for Commit BLAKE3 key", overlayId)
2321    /// hash will be different than for ForwardedPeerAdvertV0 so that core brokers dealing with public sites wont be able to correlate commits and editing peers (via common author's hash).
2322    /// only the brokers of the authors that pin a repo for outeroverlay exposure, will be able to correlate.
2323    /// it also is a different hash than the InboxId, and the OuterOverlayId, which is good to prevent correlation when the RepoId is used as author (for Repository, RootBranch and Branch commits)
2324    pub author: Digest,
2325
2326    // Peer's sequence number
2327    // pub seq: u64,
2328    /// BranchId the commit belongs to (not a ref, as readers do not need to access the branch definition)
2329    pub branch: BranchId,
2330
2331    /// optional list of dependencies on some commits in the root branch that contain the write permission needed for this commit
2332    pub perms: Vec<ObjectId>,
2333
2334    /// Keys to be able to open all the references (deps, acks, files, etc...)
2335    pub header_keys: Option<CommitHeaderKeys>,
2336
2337    /// This commit can only be accepted if signed by this quorum
2338    pub quorum: QuorumType,
2339
2340    /// App-specific metadata (commit message, creation time, etc)
2341    #[serde(with = "serde_bytes")]
2342    pub metadata: Vec<u8>,
2343
2344    /// reference to an Object with a CommitBody inside.
2345    /// When the commit is reverted or erased (after compaction/snapshot), the CommitBody is deleted, creating a dangling reference
2346    pub body: ObjectRef,
2347}
2348
2349/// Content of a Commit
2350#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2351pub enum CommitContent {
2352    V0(CommitContentV0),
2353}
2354
2355impl CommitContent {
2356    pub fn header_keys(&self) -> &Option<CommitHeaderKeys> {
2357        match self {
2358            CommitContent::V0(v0) => &v0.header_keys,
2359        }
2360    }
2361    pub fn author(&self) -> &Digest {
2362        match self {
2363            CommitContent::V0(v0) => &v0.author,
2364        }
2365    }
2366    pub fn branch(&self) -> &BranchId {
2367        match self {
2368            CommitContent::V0(v0) => &v0.branch,
2369        }
2370    }
2371
2372    pub fn author_digest(author: &UserId, overlay: OverlayId) -> Digest {
2373        let author_id = serde_bare::to_vec(author).unwrap();
2374        let overlay_id = serde_bare::to_vec(&overlay).unwrap();
2375        let mut key: [u8; 32] = blake3::derive_key(
2376            "NextGraph UserId Hash Overlay Id for Commit BLAKE3 key",
2377            overlay_id.as_slice(),
2378        );
2379        let key_hash = blake3::keyed_hash(&key, &author_id);
2380        key.zeroize();
2381        Digest::from_slice(*key_hash.as_bytes())
2382    }
2383}
2384
2385/// Commit object
2386///
2387/// Signed by member key authorized to publish this commit type
2388#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2389pub struct CommitV0 {
2390    /// ID of containing Object
2391    #[serde(skip)]
2392    pub id: Option<ObjectId>,
2393
2394    /// Key of containing Object
2395    #[serde(skip)]
2396    pub key: Option<SymKey>,
2397
2398    /// optional Commit Header
2399    #[serde(skip)]
2400    pub header: Option<CommitHeader>,
2401
2402    /// optional Commit Body
2403    #[serde(skip)]
2404    pub body: OnceCell<CommitBody>,
2405
2406    /// optional List of blocks, including the header and body ones. First one is the ObjectId of commit. Vec is ready to be sent in Event
2407    #[serde(skip)]
2408    pub blocks: Vec<BlockId>,
2409
2410    /// Commit content
2411    pub content: CommitContent,
2412
2413    /// Signature over the content (a CommitContent) by the author. an editor (userId)
2414    pub sig: Sig,
2415}
2416
2417/// Commit Object
2418#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2419pub enum Commit {
2420    V0(CommitV0),
2421}
2422
2423/// File Object
2424#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2425pub struct SmallFileV0 {
2426    pub content_type: String,
2427
2428    #[serde(with = "serde_bytes")]
2429    pub metadata: Vec<u8>,
2430
2431    #[serde(with = "serde_bytes")]
2432    pub content: Vec<u8>,
2433}
2434
2435/// A file stored in an Object
2436#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2437pub enum SmallFile {
2438    V0(SmallFileV0),
2439}
2440
2441/// Random Access File Object
2442#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2443pub struct RandomAccessFileMetaV0 {
2444    pub content_type: String,
2445
2446    #[serde(with = "serde_bytes")]
2447    pub metadata: Vec<u8>,
2448
2449    pub total_size: u64,
2450
2451    pub chunk_size: u32,
2452
2453    pub arity: u16,
2454
2455    pub depth: u8,
2456}
2457
2458/// A Random Access file stored in an Object
2459#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2460pub enum RandomAccessFileMeta {
2461    V0(RandomAccessFileMetaV0),
2462}
2463
2464impl RandomAccessFileMeta {
2465    pub fn arity(&self) -> u16 {
2466        match self {
2467            Self::V0(v0) => v0.arity,
2468        }
2469    }
2470
2471    pub fn depth(&self) -> u8 {
2472        match self {
2473            Self::V0(v0) => v0.depth,
2474        }
2475    }
2476
2477    pub fn set_depth(&mut self, depth: u8) {
2478        match self {
2479            Self::V0(v0) => {
2480                v0.depth = depth;
2481            }
2482        }
2483    }
2484
2485    pub fn chunk_size(&self) -> u32 {
2486        match self {
2487            Self::V0(v0) => v0.chunk_size,
2488        }
2489    }
2490
2491    pub fn total_size(&self) -> u64 {
2492        match self {
2493            Self::V0(v0) => v0.total_size,
2494        }
2495    }
2496
2497    pub fn set_total_size(&mut self, size: u64) {
2498        match self {
2499            Self::V0(v0) => {
2500                v0.total_size = size;
2501            }
2502        }
2503    }
2504
2505    pub fn metadata(&self) -> &Vec<u8> {
2506        match self {
2507            Self::V0(v0) => &v0.metadata,
2508        }
2509    }
2510
2511    pub fn content_type(&self) -> &String {
2512        match self {
2513            Self::V0(v0) => &v0.content_type,
2514        }
2515    }
2516}
2517
2518/// Immutable data stored encrypted in a Merkle tree V0
2519#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2520pub enum ObjectContentV0 {
2521    Commit(Commit),
2522    CommitBody(CommitBody),
2523    CommitHeader(CommitHeader),
2524    Quorum(Quorum),
2525    Signature(Signature),
2526    Certificate(Certificate),
2527    SmallFile(SmallFile),
2528    RandomAccessFileMeta(RandomAccessFileMeta),
2529    RefreshCap(RefreshCap),
2530}
2531
2532/// Immutable data stored encrypted in a Merkle tree
2533#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
2534pub enum ObjectContent {
2535    V0(ObjectContentV0),
2536}
2537
2538//
2539// COMMON TYPES FOR MESSAGES
2540//
2541
2542pub trait IObject {
2543    fn block_ids(&self) -> Vec<BlockId>;
2544
2545    fn id(&self) -> Option<ObjectId>;
2546
2547    fn key(&self) -> Option<SymKey>;
2548}
2549
2550pub type DirectPeerId = PubKey;
2551
2552pub type ForwardedPeerId = PubKey;
2553
2554/// Peer ID: public key of the node, or an encrypted version of it
2555#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
2556pub enum PeerId {
2557    Direct(DirectPeerId),
2558    Forwarded(ForwardedPeerId),
2559    /// BLAKE3 keyed hash over ForwardedPeerId
2560    /// - key: BLAKE3 derive_key ("NextGraph ForwardedPeerId Hash Overlay Id BLAKE3 key", overlayId)
2561    ForwardedObfuscated(Digest),
2562}
2563
2564impl fmt::Display for PeerId {
2565    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2566        match self {
2567            Self::Direct(p) => {
2568                write!(f, "Direct    : {}", p)
2569            }
2570            Self::Forwarded(p) => {
2571                write!(f, "Forwarded    : {}", p)
2572            }
2573            Self::ForwardedObfuscated(p) => {
2574                write!(f, "ForwardedObfuscated    : {}", p)
2575            }
2576        }
2577    }
2578}
2579
2580impl PeerId {
2581    pub fn get_pub_key(&self) -> PubKey {
2582        match self {
2583            Self::Direct(pk) | Self::Forwarded(pk) => pk.clone(),
2584            _ => panic!("cannot get a pubkey for ForwardedObfuscated"),
2585        }
2586    }
2587}
2588
2589/// Content of EventV0
2590///
2591/// Contains the objects of newly published Commit, its optional blocks, and optional FILES and their blocks.
2592/// If a block is not present in the Event, its ID should be present in block_ids and the block should be put on the emitting broker beforehand with BlocksPut.
2593#[derive(Clone, Debug, Serialize, Deserialize)]
2594pub struct EventContentV0 {
2595    /// Pub/sub topic
2596    pub topic: TopicId,
2597
2598    // TODO: could be obfuscated (or not, if we want to be able to recall events)
2599    // on public repos, should be obfuscated
2600    pub publisher: PeerId,
2601
2602    /// Commit sequence number of publisher
2603    pub seq: u64,
2604
2605    /// Blocks with encrypted content. First in the list is always the commit block followed by its children, then its optional header and body blocks (and eventual children),
2606    /// blocks of the FILES are optional (only sent here if user specifically want to push them to the pub/sub).
2607    /// the first in the list MUST contain a commit_header_key
2608    /// When saved locally (the broker keeps the associated event, until the topic is refreshed(the last heads retain their events) ),
2609    /// so, this `blocks` list is emptied (as the blocked are saved in the overlay storage anyway) and their IDs are kept on the side.
2610    /// then when the event needs to be send in reply to a *TopicSyncReq, the blocks list is regenerated from the IDs,
2611    /// so that a valid EventContent can be sent (and so that its signature can be verified successfully)
2612    pub blocks: Vec<Block>,
2613
2614    /// Ids of additional Blocks (FILES) with encrypted content that are not to be pushed in the pub/sub
2615    /// they will be retrieved later by interested users
2616    pub file_ids: Vec<BlockId>,
2617
2618    /// can be :
2619    /// * Encrypted key for the Commit object (the first Block in blocks vec)
2620    ///   The ObjectKey is encrypted using ChaCha20:
2621    ///   - key: BLAKE3 derive_key ("NextGraph Event Commit ObjectKey ChaCha20 key",
2622    ///                             RepoId + BranchId + branch_secret(ReadCapSecret of the branch) + publisher)
2623    ///   - nonce: commit_seq
2624    /// * If it is a CertificateRefresh, both the blocks and block_ids vectors are empty.
2625    ///   the key here contains an encrypted ObjectRef to the new Certificate.
2626    ///   The whole ObjectRef is encrypted (including the ID) to avoid correlation of topics who will have the same Certificate ID (belong to the same repo)
2627    ///   Encrypted using ChaCha20, with :
2628    ///   - key: BLAKE3 derive_key ("NextGraph Event Certificate ObjectRef ChaCha20 key",
2629    ///                             RepoId + BranchId + branch_secret(ReadCapSecret of the branch) + publisher)
2630    ///                             it is the same key as above, because the commit_seq will be different (incremented anyway)
2631    ///   - nonce: commit_seq
2632    #[serde(with = "serde_bytes")]
2633    pub key: Vec<u8>,
2634}
2635
2636/// Pub/sub event published in a topic
2637///
2638/// Forwarded along event routing table entries
2639#[derive(Clone, Debug, Serialize, Deserialize)]
2640pub struct EventV0 {
2641    pub content: EventContentV0,
2642
2643    /// Signature over content by topic key
2644    pub topic_sig: Sig,
2645
2646    /// Signature over content by publisher PeerID priv key
2647    pub peer_sig: Sig,
2648}
2649
2650/// Pub/sub event published in a topic
2651#[derive(Clone, Debug, Serialize, Deserialize)]
2652pub enum Event {
2653    V0(EventV0),
2654}