ng_repo/
types.rs

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