ng_repo/
commit.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//! Commit that composes the DAG of a Branch
11
12use core::fmt;
13use std::collections::HashSet;
14use std::iter::FromIterator;
15
16use ed25519_dalek::{PublicKey, Signature};
17use once_cell::sync::OnceCell;
18
19use crate::errors::*;
20#[allow(unused_imports)]
21use crate::log::*;
22use crate::object::*;
23use crate::repo::CommitInfo;
24use crate::repo::Repo;
25use crate::store::Store;
26use crate::types::*;
27use crate::utils::*;
28
29#[derive(Debug, PartialEq, Eq, Clone)]
30pub enum CommitLoadError {
31    MissingBlocks(Vec<BlockId>),
32    ObjectParseError,
33    NotACommit,
34    NotACommitBody,
35    CannotBeAtRootOfBranch,
36    MustBeAtRootOfBranch,
37    SingletonCannotHaveHeader,
38    MalformedHeader,
39    BodyTypeMismatch,
40    ContentParseError(ObjectParseError),
41}
42
43#[derive(Debug, PartialEq, Eq, Clone)]
44pub enum CommitVerifyError {
45    InvalidSignature,
46    InvalidHeader,
47    PermissionDenied,
48}
49
50impl CommitV0 {
51    /// New commit
52    pub fn new(
53        author_privkey: &PrivKey,
54        author_pubkey: &PubKey,
55        overlay: OverlayId,
56        branch: BranchId,
57        quorum: QuorumType,
58        deps: Vec<ObjectRef>,
59        ndeps: Vec<ObjectRef>,
60        acks: Vec<ObjectRef>,
61        nacks: Vec<ObjectRef>,
62        files: Vec<ObjectRef>,
63        nfiles: Vec<ObjectRef>,
64        metadata: Vec<u8>,
65        body: ObjectRef,
66    ) -> Result<CommitV0, NgError> {
67        let headers = CommitHeader::new_with(deps, ndeps, acks, nacks, files, nfiles);
68        let content = CommitContent::V0(CommitContentV0 {
69            perms: vec![],
70            author: CommitContent::author_digest(author_pubkey, overlay),
71            branch,
72            header_keys: headers.1,
73            quorum,
74            timestamp: now_timestamp(),
75            metadata,
76            body,
77        });
78        let content_ser = serde_bare::to_vec(&content).unwrap();
79
80        // sign commit
81        let sig = sign(author_privkey, author_pubkey, &content_ser)?;
82        Ok(CommitV0 {
83            content: content,
84            sig,
85            id: None,
86            key: None,
87            header: headers.0,
88            body: OnceCell::new(),
89            blocks: vec![],
90        })
91    }
92
93    #[cfg(test)]
94    /// New commit with invalid header, only for test purposes
95    pub fn new_with_invalid_header(
96        author_privkey: &PrivKey,
97        author_pubkey: &PubKey,
98        branch: BranchId,
99        quorum: QuorumType,
100        metadata: Vec<u8>,
101        body: ObjectRef,
102    ) -> Result<CommitV0, NgError> {
103        let headers = CommitHeader::new_invalid();
104        let content = CommitContent::V0(CommitContentV0 {
105            perms: vec![],
106            author: CommitContent::author_digest(&author_pubkey, OverlayId::dummy()),
107            branch,
108            header_keys: headers.1,
109            quorum,
110            timestamp: now_timestamp(),
111            metadata,
112            body,
113        });
114        let content_ser = serde_bare::to_vec(&content).unwrap();
115
116        // sign commit
117        let sig = sign(&author_privkey, &author_pubkey, &content_ser)?;
118        Ok(CommitV0 {
119            content: content,
120            sig,
121            id: None,
122            key: None,
123            header: headers.0,
124            body: OnceCell::new(),
125            blocks: vec![],
126        })
127    }
128
129    pub fn save(&mut self, block_size: usize, store: &Store) -> Result<ObjectRef, StorageError> {
130        if self.id.is_some() && self.key.is_some() {
131            return Ok(ObjectRef::from_id_key(
132                self.id.unwrap(),
133                self.key.as_ref().unwrap().clone(),
134            ));
135        }
136        // log_debug!("{:?}", self.header);
137        let mut obj = Object::new(
138            ObjectContent::V0(ObjectContentV0::Commit(Commit::V0(self.clone()))),
139            self.header.clone(),
140            block_size,
141            store,
142        );
143        self.blocks = obj.save(store)?;
144        if let Some(h) = &mut self.header {
145            if let Some(id) = obj.header().as_ref().unwrap().id() {
146                h.set_id(*id);
147            }
148        }
149        self.id = Some(obj.get_and_save_id());
150        self.key = Some(obj.key().unwrap());
151        Ok(obj.reference().unwrap())
152    }
153}
154
155impl IObject for Commit {
156    fn block_ids(&self) -> Vec<BlockId> {
157        self.blocks().clone()
158    }
159    /// Get ID of including `Object`,
160    /// only available if the Commit was loaded from store or saved
161    fn id(&self) -> Option<ObjectId> {
162        match self {
163            Commit::V0(c) => c.id,
164        }
165    }
166
167    /// Get key of including `Object`
168    /// only available if the Commit was loaded from store or saved
169    fn key(&self) -> Option<SymKey> {
170        match self {
171            Commit::V0(c) => c.key.clone(),
172        }
173    }
174}
175
176impl Commit {
177    /// New commit
178    pub fn new(
179        author_privkey: &PrivKey,
180        author_pubkey: &PubKey,
181        overlay: OverlayId,
182        branch: BranchId,
183        quorum: QuorumType,
184        deps: Vec<ObjectRef>,
185        ndeps: Vec<ObjectRef>,
186        acks: Vec<ObjectRef>,
187        nacks: Vec<ObjectRef>,
188        files: Vec<ObjectRef>,
189        nfiles: Vec<ObjectRef>,
190        metadata: Vec<u8>,
191        body: ObjectRef,
192    ) -> Result<Commit, NgError> {
193        CommitV0::new(
194            author_privkey,
195            author_pubkey,
196            overlay,
197            branch,
198            quorum,
199            deps,
200            ndeps,
201            acks,
202            nacks,
203            files,
204            nfiles,
205            metadata,
206            body,
207        )
208        .map(|c| Commit::V0(c))
209    }
210
211    /// New commit with a body. everything is saved
212    pub fn new_with_body_acks_deps_and_save(
213        author_privkey: &PrivKey,
214        author_pubkey: &PubKey,
215        branch: BranchId,
216        quorum: QuorumType,
217        deps: Vec<ObjectRef>,
218        acks: Vec<ObjectRef>,
219        body: CommitBody,
220        store: &Store,
221    ) -> Result<Commit, NgError> {
222        Self::new_with_body_and_save(
223            author_privkey,
224            author_pubkey,
225            branch,
226            quorum,
227            deps,
228            vec![],
229            acks,
230            vec![],
231            vec![],
232            vec![],
233            vec![],
234            body,
235            0,
236            store,
237        )
238    }
239
240    /// New commit with a body. everything is saved
241    pub fn new_with_body_and_save(
242        author_privkey: &PrivKey,
243        author_pubkey: &PubKey,
244        branch: BranchId,
245        quorum: QuorumType,
246        deps: Vec<ObjectRef>,
247        ndeps: Vec<ObjectRef>,
248        acks: Vec<ObjectRef>,
249        nacks: Vec<ObjectRef>,
250        files: Vec<ObjectRef>,
251        nfiles: Vec<ObjectRef>,
252        metadata: Vec<u8>,
253        body: CommitBody,
254        block_size: usize,
255        store: &Store,
256    ) -> Result<Commit, NgError> {
257        let (body_ref, mut saved_body) = body.clone().save(block_size, store)?;
258        let overlay = store.get_store_repo().overlay_id_for_read_purpose();
259        let mut commit_v0 = CommitV0::new(
260            author_privkey,
261            author_pubkey,
262            overlay,
263            branch,
264            quorum,
265            deps,
266            ndeps,
267            acks,
268            nacks,
269            files,
270            nfiles,
271            metadata,
272            body_ref,
273        )?;
274        commit_v0.body.set(body).unwrap();
275        let _commit_ref = commit_v0.save(block_size, store)?;
276        commit_v0.blocks.append(&mut saved_body);
277
278        Ok(Commit::V0(commit_v0))
279    }
280
281    pub fn reference(&self) -> Option<ObjectRef> {
282        if self.key().is_some() && self.id().is_some() {
283            Some(ObjectRef {
284                id: self.id().unwrap(),
285                key: self.key().unwrap(),
286            })
287        } else {
288            None
289        }
290    }
291
292    pub fn save(&mut self, block_size: usize, store: &Store) -> Result<ObjectRef, StorageError> {
293        match self {
294            Commit::V0(v0) => v0.save(block_size, store),
295        }
296    }
297
298    pub fn blocks(&self) -> &Vec<BlockId> {
299        match self {
300            Commit::V0(v0) => &v0.blocks,
301        }
302    }
303
304    #[cfg(test)]
305    fn empty_blocks(&mut self) {
306        match self {
307            Commit::V0(v0) => v0.blocks = vec![],
308        }
309    }
310
311    /// Load commit from store
312    pub fn load(
313        commit_ref: ObjectRef,
314        store: &Store,
315        with_body: bool,
316    ) -> Result<Commit, CommitLoadError> {
317        let (id, key) = (commit_ref.id, commit_ref.key);
318        match Object::load(id, Some(key.clone()), store) {
319            Err(ObjectParseError::MissingHeaderBlocks((obj, mut missing))) => {
320                //log_debug!("MISSING {:?}", missing);
321                if with_body {
322                    let content = obj
323                        .content()
324                        .map_err(|e| CommitLoadError::ContentParseError(e))?;
325                    let mut commit = match content {
326                        ObjectContent::V0(ObjectContentV0::Commit(c)) => c,
327                        _ => return Err(CommitLoadError::NotACommit),
328                    };
329                    commit.set_id(id);
330                    commit.set_key(key);
331                    match commit.load_body(store) {
332                        Ok(_) => return Err(CommitLoadError::MissingBlocks(missing)),
333                        Err(CommitLoadError::MissingBlocks(mut missing_body)) => {
334                            missing.append(&mut missing_body);
335                            return Err(CommitLoadError::MissingBlocks(missing));
336                        }
337                        Err(e) => return Err(e),
338                    }
339                } else {
340                    return Err(CommitLoadError::MissingBlocks(missing));
341                }
342            }
343            Ok(obj) => {
344                let content = obj
345                    .content()
346                    .map_err(|e| CommitLoadError::ContentParseError(e))?;
347                let mut commit = match content {
348                    ObjectContent::V0(ObjectContentV0::Commit(c)) => c,
349                    _ => return Err(CommitLoadError::NotACommit),
350                };
351                commit.set_id(id);
352                commit.set_key(key);
353                commit.set_header(obj.header().clone());
354
355                if with_body {
356                    commit.load_body(store)?;
357                }
358
359                Ok(commit)
360            }
361            Err(ObjectParseError::MissingBlocks(missing)) => {
362                Err(CommitLoadError::MissingBlocks(missing))
363            }
364            Err(_e) => {
365                log_err!("{:?}", _e);
366                Err(CommitLoadError::ObjectParseError)
367            }
368        }
369    }
370
371    /// Load commit body from store
372    pub fn load_body(&self, store: &Store) -> Result<&CommitBody, CommitLoadError> {
373        if self.body().is_some() {
374            return Ok(self.body().unwrap());
375        }
376        let content = self.content_v0();
377        let (id, key) = (content.body.id, content.body.key.clone());
378        let obj = Object::load(id.clone(), Some(key.clone()), store).map_err(|e| match e {
379            ObjectParseError::MissingBlocks(missing) => CommitLoadError::MissingBlocks(missing),
380            _ => CommitLoadError::ObjectParseError,
381        })?;
382        let content = obj
383            .content()
384            .map_err(|_e| CommitLoadError::ObjectParseError)?;
385        match content {
386            ObjectContent::V0(ObjectContentV0::CommitBody(body)) => {
387                self.set_body(body);
388                Ok(self.body().unwrap())
389            }
390            _ => Err(CommitLoadError::NotACommitBody),
391        }
392    }
393
394    fn set_body(&self, body: CommitBody) {
395        match self {
396            Commit::V0(c) => {
397                c.body.set(body).unwrap();
398            }
399        }
400    }
401
402    /// Get ID of header `Object`
403    pub fn header_id(&self) -> &Option<ObjectId> {
404        match self {
405            Commit::V0(CommitV0 {
406                header: Some(ch), ..
407            }) => ch.id(),
408            _ => &None,
409        }
410    }
411
412    /// Set ID of including `Object`
413    fn set_id(&mut self, id: ObjectId) {
414        match self {
415            Commit::V0(c) => c.id = Some(id),
416        }
417    }
418
419    /// Set key of including `Object`
420    fn set_key(&mut self, key: SymKey) {
421        match self {
422            Commit::V0(c) => c.key = Some(key),
423        }
424    }
425
426    /// Set header of including `Object`
427    fn set_header(&mut self, header: Option<CommitHeader>) {
428        match self {
429            Commit::V0(c) => c.header = header,
430        }
431    }
432
433    /// Get commit signature
434    pub fn sig(&self) -> &Sig {
435        match self {
436            Commit::V0(c) => &c.sig,
437        }
438    }
439
440    /// Get author (a UserId)
441    pub fn author(&self) -> &Digest {
442        self.content().author()
443    }
444
445    pub fn timestamp(&self) -> Timestamp {
446        self.content().timestamp()
447    }
448
449    pub fn final_consistency(&self) -> bool {
450        self.content().final_consistency()
451            || self.body().is_some_and(|body| body.total_order_required())
452    }
453
454    pub fn get_type(&self) -> Option<CommitType> {
455        self.body().map(|b| b.get_type())
456    }
457
458    pub fn get_signature_reference(&self) -> Option<ObjectRef> {
459        self.body().map_or(None, |b| b.get_signature_reference())
460    }
461
462    pub fn as_info(&self, repo: &Repo) -> CommitInfo {
463        let past = self.acks_ids();
464        // past.sort();
465        // let branch = past
466        //     .is_empty()
467        //     .then_some(self.id().unwrap())
468        //     .or(Some(past[0]));
469        CommitInfo {
470            past,
471            key: self.key().unwrap(),
472            signature: None,
473            author: repo.get_user_string(self.author()),
474            timestamp: self.timestamp(),
475            final_consistency: self.final_consistency(),
476            commit_type: self.get_type().unwrap(),
477            branch: None,
478            x: 0,
479            y: 0,
480        }
481    }
482
483    /// Get branch ID this commit is about
484    pub fn branch(&self) -> &BranchId {
485        self.content().branch()
486    }
487
488    /// Get commit header
489    pub fn header(&self) -> &Option<CommitHeader> {
490        match self {
491            Commit::V0(c) => &c.header,
492        }
493    }
494
495    pub fn body_ref(&self) -> &ObjectRef {
496        &self.content_v0().body
497    }
498
499    /// Get commit content V0
500    pub fn content_v0(&self) -> &CommitContentV0 {
501        match self {
502            Commit::V0(CommitV0 {
503                content: CommitContent::V0(c),
504                ..
505            }) => c,
506        }
507    }
508
509    /// Get quorum_type
510    pub fn quorum_type(&self) -> &QuorumType {
511        &self.content_v0().quorum
512    }
513
514    /// Get commit content
515    pub fn content(&self) -> &CommitContent {
516        match self {
517            Commit::V0(CommitV0 { content: c, .. }) => c,
518        }
519    }
520
521    pub fn body(&self) -> Option<&CommitBody> {
522        match self {
523            Commit::V0(c) => c.body.get(),
524        }
525    }
526
527    pub fn owners_signature_required(&self, store: &Store) -> Result<bool, CommitLoadError> {
528        match self.load_body(store)? {
529            CommitBody::V0(CommitBodyV0::UpdateRootBranch(new_root)) => {
530                // load deps (the previous RootBranch commit)
531                let deps = self.deps();
532                if deps.len() != 1 {
533                    Err(CommitLoadError::MalformedHeader)
534                } else {
535                    let previous_rootbranch_commit = Commit::load(deps[0].clone(), store, true)?;
536                    let previous_rootbranch = previous_rootbranch_commit
537                        .body()
538                        .unwrap()
539                        .root_branch_commit()?;
540                    if previous_rootbranch.owners() != new_root.owners() {
541                        Ok(true)
542                    } else {
543                        Ok(false)
544                    }
545                }
546            }
547            CommitBody::V0(CommitBodyV0::RootBranch(_)) => {
548                let deps = self.deps();
549                let acks = self.acks();
550                if deps.is_empty() && acks.len() == 1 {
551                    // we check that the ACK is the repository singleton commit. in this case, it means we are dealing with the first RootBranch commit, which is fine to have no deps.
552                    let causal_past = Commit::load(acks[0].clone(), store, true)?;
553                    if causal_past.body().unwrap().is_repository_singleton_commit() {
554                        return Ok(false);
555                    }
556                }
557                Err(CommitLoadError::MalformedHeader)
558            }
559            CommitBody::V0(CommitBodyV0::Delete(_)) => Ok(true),
560            _ => Ok(false),
561        }
562    }
563
564    /// This commit is the first one in the branch (doesn't have any ACKs nor Nacks)
565    pub fn is_root_commit_of_branch(&self) -> bool {
566        match self {
567            Commit::V0(CommitV0 {
568                content: CommitContent::V0(c),
569                ..
570            }) => match &c.header_keys {
571                Some(CommitHeaderKeys::V0(hk)) => hk.acks.is_empty() && hk.nacks.is_empty(),
572                None => true,
573            },
574        }
575    }
576
577    /// Get acks (that have both an ID in the header and a key in the header_keys)
578    pub fn acks(&self) -> Vec<ObjectRef> {
579        let mut res: Vec<ObjectRef> = vec![];
580        match self {
581            Commit::V0(c) => match &c.header {
582                Some(CommitHeader::V0(header_v0)) => match &c.content.header_keys() {
583                    Some(CommitHeaderKeys::V0(hk_v0)) => {
584                        for ack in header_v0.acks.iter().zip(hk_v0.acks.iter()) {
585                            res.push(ack.into());
586                        }
587                    }
588                    None => {}
589                },
590                None => {}
591            },
592        };
593        res
594    }
595
596    /// Get acks (that have an ID in the header, without checking if there is a key for them in the header_keys)
597    /// if there is no header, returns an empty vec
598    pub fn acks_ids(&self) -> Vec<ObjectId> {
599        match self {
600            Commit::V0(c) => match &c.header {
601                Some(h) => h.acks(),
602                None => vec![],
603            },
604        }
605    }
606
607    /// Get deps (that have an ID in the header, without checking if there is a key for them in the header_keys)
608    /// if there is no header, returns an empty vec
609    pub fn deps_ids(&self) -> Vec<ObjectId> {
610        match self {
611            Commit::V0(c) => match &c.header {
612                Some(h) => h.deps(),
613                None => vec![],
614            },
615        }
616    }
617
618    /// Get files
619    pub fn files(&self) -> Vec<ObjectRef> {
620        let mut res: Vec<ObjectRef> = vec![];
621        match self {
622            Commit::V0(c) => match &c.content.header_keys() {
623                Some(CommitHeaderKeys::V0(hk_v0)) => {
624                    for file in hk_v0.files.iter() {
625                        res.push(file.clone());
626                    }
627                }
628                None => {}
629            },
630        };
631        res
632    }
633
634    /// Get deps (that have both an ID in the header and a key in the header_keys)
635    pub fn deps(&self) -> Vec<ObjectRef> {
636        let mut res: Vec<ObjectRef> = vec![];
637        match self {
638            Commit::V0(c) => match &c.header {
639                Some(CommitHeader::V0(header_v0)) => match &c.content.header_keys() {
640                    Some(CommitHeaderKeys::V0(hk_v0)) => {
641                        for dep in header_v0.deps.iter().zip(hk_v0.deps.iter()) {
642                            res.push(dep.into());
643                        }
644                    }
645                    None => {}
646                },
647                None => {}
648            },
649        };
650        res
651    }
652
653    /// Get all commits that are in the direct causal past of the commit (`acks` and `nacks`)
654    /// only returns objectRefs that have both an ID from header and a KEY from header_keys (they all have a key)
655    pub fn direct_causal_past(&self) -> Vec<ObjectRef> {
656        let mut res: Vec<ObjectRef> = vec![];
657        match self {
658            Commit::V0(c) => match (&c.header, &c.content.header_keys()) {
659                (Some(CommitHeader::V0(header_v0)), Some(CommitHeaderKeys::V0(hk_v0))) => {
660                    for ack in header_v0.acks.iter().zip(hk_v0.acks.iter()) {
661                        res.push(ack.into());
662                    }
663                    for nack in header_v0.nacks.iter().zip(hk_v0.nacks.iter()) {
664                        res.push(nack.into());
665                    }
666                }
667                _ => {}
668            },
669        };
670        res
671    }
672
673    pub fn direct_causal_past_ids(&self) -> HashSet<ObjectId> {
674        let mut res: HashSet<ObjectId> = HashSet::with_capacity(1);
675        match self {
676            Commit::V0(c) => match &c.header {
677                Some(CommitHeader::V0(header_v0)) => {
678                    res.extend(header_v0.acks.iter());
679                    res.extend(header_v0.nacks.iter());
680                }
681                _ => {}
682            },
683        };
684        res
685    }
686
687    // /// Get seq
688    // pub fn seq(&self) -> u64 {
689    //     match self {
690    //         Commit::V0(CommitV0 {
691    //             content: CommitContent::V0(c),
692    //             ..
693    //         }) => c.seq,
694    //     }
695    // }
696
697    /// Verify commit signature
698    pub fn verify_sig(&self, repo: &Repo) -> Result<(), CommitVerifyError> {
699        let c = match self {
700            Commit::V0(c) => c,
701        };
702        let content_ser = serde_bare::to_vec(&c.content).unwrap();
703
704        let pubkey = repo
705            .member_pubkey(c.content.author())
706            .map_err(|_| CommitVerifyError::PermissionDenied)?;
707
708        let pubkey_slice = match pubkey {
709            PubKey::Ed25519PubKey(pk) => pk,
710            _ => panic!("author cannot have a Montgomery key"),
711        };
712        let pk = PublicKey::from_bytes(&pubkey_slice)
713            .map_err(|_| CommitVerifyError::InvalidSignature)?;
714        let sig_bytes = match c.sig {
715            Sig::Ed25519Sig(ss) => [ss[0], ss[1]].concat(),
716        };
717        let sig =
718            Signature::from_bytes(&sig_bytes).map_err(|_| CommitVerifyError::InvalidSignature)?;
719        pk.verify_strict(&content_ser, &sig)
720            .map_err(|_| CommitVerifyError::InvalidSignature)
721    }
722
723    /// Verify commit permissions
724    pub fn verify_perm(&self, repo: &Repo) -> Result<(), NgError> {
725        repo.verify_permission(self)
726    }
727
728    pub fn verify_perm_creation(&self, user: Option<&Digest>) -> Result<&Digest, NgError> {
729        let digest = self.content().author();
730        if user.is_some() && *digest != *user.unwrap() {
731            return Err(NgError::PermissionDenied);
732        }
733        let body = self.body().ok_or(NgError::InvalidArgument)?;
734        if !(body.is_repository_singleton_commit() && user.is_none()) {
735            // a user must be provided to verify all subsequent commits of a Repository commit, that have the same author and that are signed with the repository key
736            return Err(NgError::InvalidArgument);
737        }
738        if body.required_permission().contains(&PermissionV0::Create) {
739            Ok(digest)
740        } else {
741            Err(NgError::PermissionDenied)
742        }
743    }
744
745    /// Verify if the commit's `body` and its direct_causal_past, and recursively all their refs are available in the `store`
746    /// returns a list of all the ObjectIds that have been visited (only commits in the DAG)
747    /// or a list of missing blocks
748    pub fn verify_full_object_refs_of_branch_at_commit(
749        &self,
750        store: &Store,
751    ) -> Result<Vec<ObjectId>, CommitLoadError> {
752        //log_debug!(">> verify_full_object_refs_of_branch_at_commit: #{}", self.seq());
753
754        /// Load `Commit`s of a `Branch` from the `Store` starting from the given `Commit`,
755        /// and collect missing `ObjectId`s
756        fn load_direct_object_refs(
757            commit: &Commit,
758            store: &Store,
759            visited: &mut HashSet<ObjectId>,
760            missing: &mut HashSet<ObjectId>,
761        ) -> Result<(), CommitLoadError> {
762            //log_debug!(">>> load_branch: #{}", commit.seq());
763
764            // if the self of verify_full_object_refs_of_branch_at_commit() has not been saved yet, then it doesn't have an ID
765            match commit.id() {
766                Some(id) => {
767                    if visited.contains(&id) {
768                        return Ok(());
769                    }
770                    visited.insert(id);
771                    // not adding the ObjectId of the header of this commit as it is not part of the DAG (neither is the CommitBody added to visited)
772                    // // commit.header_id().map(|hid| visited.insert(hid));
773                }
774                None => {
775                    if !visited.is_empty() {
776                        // we are not at the beginning (meaning, the self/the commit object) so this is a panic error as all causal
777                        // past commits have been loaded from store and should have an id
778                        panic!("A Commit in the causal past doesn't have an ID");
779                    }
780                }
781            }
782
783            // load body & check if it's the Branch root commit
784            match commit.load_body(store) {
785                Ok(_) => Ok(()),
786                Err(CommitLoadError::MissingBlocks(m)) => {
787                    // The commit body is missing.
788                    missing.extend(m.clone());
789                    Err(CommitLoadError::MissingBlocks(m))
790                }
791                Err(e) => Err(e),
792            }?;
793
794            let body = commit.body().unwrap();
795            visited.insert(commit.content_v0().body.id);
796            if commit.is_root_commit_of_branch() {
797                if !body.must_be_root_commit_in_branch() {
798                    return Err(CommitLoadError::CannotBeAtRootOfBranch);
799                }
800                if body.is_repository_singleton_commit() && commit.header().is_some() {
801                    return Err(CommitLoadError::SingletonCannotHaveHeader);
802                }
803            } else {
804                if body.must_be_root_commit_in_branch() {
805                    return Err(CommitLoadError::MustBeAtRootOfBranch);
806                }
807            }
808
809            // load direct causal past
810            for blockref in commit.direct_causal_past() {
811                match Commit::load(blockref, store, true) {
812                    Ok(mut c) => {
813                        load_direct_object_refs(&mut c, store, visited, missing)?;
814                    }
815                    Err(CommitLoadError::MissingBlocks(m)) => {
816                        missing.extend(m);
817                    }
818                    Err(e) => return Err(e),
819                }
820            }
821
822            Ok(())
823        }
824
825        let mut visited = HashSet::new();
826        let mut missing = HashSet::new();
827        load_direct_object_refs(self, store, &mut visited, &mut missing)?;
828
829        if !missing.is_empty() {
830            return Err(CommitLoadError::MissingBlocks(Vec::from_iter(missing)));
831        }
832        Ok(Vec::from_iter(visited))
833    }
834
835    /// Verify signature, permissions, and full causal past
836    pub fn verify(&self, repo: &Repo) -> Result<(), NgError> {
837        if !self.header().as_ref().map_or(true, |h| h.verify()) {
838            return Err(NgError::CommitVerifyError(CommitVerifyError::InvalidHeader));
839        }
840        self.verify_sig(repo)?;
841        self.verify_perm(repo)?;
842        self.verify_full_object_refs_of_branch_at_commit(&repo.store)?;
843        Ok(())
844    }
845}
846
847impl PermissionV0 {
848    /// the kind of permissions that can be added and removed with AddWritePermission and RemoveWritePermission permissions respectively
849    pub fn is_write_permission(&self) -> bool {
850        match self {
851            Self::WriteAsync | Self::WriteSync | Self::RefreshWriteCap => true,
852            _ => false,
853        }
854    }
855
856    pub fn is_delegated_by_admin(&self) -> bool {
857        self.is_write_permission()
858            || match self {
859                Self::AddReadMember
860                | Self::RemoveMember
861                | Self::AddWritePermission
862                | Self::RemoveWritePermission
863                | Self::Compact
864                | Self::AddBranch
865                | Self::RemoveBranch
866                | Self::ChangeName
867                | Self::RefreshReadCap => true,
868                _ => false,
869            }
870    }
871
872    pub fn is_delegated_by_owner(&self) -> bool {
873        self.is_delegated_by_admin()
874            || match self {
875                Self::ChangeQuorum | Self::Admin | Self::ChangeMainBranch => true,
876                _ => false,
877            }
878    }
879}
880
881impl CommitBody {
882    pub fn save(
883        self,
884        block_size: usize,
885        store: &Store,
886    ) -> Result<(ObjectRef, Vec<BlockId>), StorageError> {
887        let obj = Object::new(
888            ObjectContent::V0(ObjectContentV0::CommitBody(self)),
889            None,
890            block_size,
891            store,
892        );
893        let blocks = obj.save(store)?;
894        Ok((obj.reference().unwrap(), blocks))
895    }
896
897    pub fn is_add_signer_cap(&self) -> bool {
898        match self {
899            Self::V0(v0) => match v0 {
900                CommitBodyV0::AddSignerCap(_) => true,
901                _ => false,
902            },
903        }
904    }
905
906    pub fn root_branch_commit(&self) -> Result<&RootBranch, CommitLoadError> {
907        match self {
908            Self::V0(v0) => match v0 {
909                CommitBodyV0::UpdateRootBranch(rb) | CommitBodyV0::RootBranch(rb) => Ok(rb),
910                _ => Err(CommitLoadError::BodyTypeMismatch),
911            },
912        }
913    }
914
915    pub fn is_repository_singleton_commit(&self) -> bool {
916        match self {
917            Self::V0(v0) => match v0 {
918                CommitBodyV0::Repository(_) => true,
919                _ => false,
920            },
921        }
922    }
923    pub fn must_be_root_commit_in_branch(&self) -> bool {
924        match self {
925            Self::V0(v0) => match v0 {
926                CommitBodyV0::Repository(_) => true,
927                CommitBodyV0::Branch(_) => true,
928                _ => false,
929            },
930        }
931    }
932
933    pub fn on_root_branch(&self) -> bool {
934        match self {
935            Self::V0(v0) => match v0 {
936                CommitBodyV0::Repository(_) => true,
937                CommitBodyV0::RootBranch(_) => true,
938                CommitBodyV0::UpdateRootBranch(_) => true,
939                CommitBodyV0::AddBranch(_) => true,
940                CommitBodyV0::RemoveBranch(_) => true,
941                CommitBodyV0::AddMember(_) => true,
942                CommitBodyV0::RemoveMember(_) => true,
943                CommitBodyV0::AddPermission(_) => true,
944                CommitBodyV0::RemovePermission(_) => true,
945                CommitBodyV0::AddName(_) => true,
946                CommitBodyV0::RemoveName(_) => true,
947                //CommitBodyV0::Quorum(_) => true,
948                CommitBodyV0::RootCapRefresh(_) => true,
949                CommitBodyV0::CapRefreshed(_) => true,
950                CommitBodyV0::SyncSignature(_) => true,
951                CommitBodyV0::Delete(_) => true,
952                _ => false,
953            },
954        }
955    }
956
957    pub fn on_transactional_branch(&self) -> bool {
958        match self {
959            Self::V0(v0) => match v0 {
960                CommitBodyV0::Branch(_) => true,
961                CommitBodyV0::UpdateBranch(_) => true,
962                CommitBodyV0::Snapshot(_) => true,
963                CommitBodyV0::AsyncTransaction(_) => true,
964                CommitBodyV0::SyncTransaction(_) => true,
965                CommitBodyV0::AddFile(_) => true,
966                CommitBodyV0::RemoveFile(_) => true,
967                CommitBodyV0::Compact(_) => true,
968                CommitBodyV0::AsyncSignature(_) => true,
969                CommitBodyV0::BranchCapRefresh(_) => true,
970                CommitBodyV0::CapRefreshed(_) => true,
971                CommitBodyV0::SyncSignature(_) => true,
972                _ => false,
973            },
974        }
975    }
976
977    pub fn on_store_branch(&self) -> bool {
978        match self {
979            Self::V0(v0) => match v0 {
980                CommitBodyV0::AddRepo(_) => true,
981                CommitBodyV0::RemoveRepo(_) => true,
982                _ => false,
983            },
984        }
985    }
986
987    pub fn on_user_branch(&self) -> bool {
988        match self {
989            Self::V0(v0) => match v0 {
990                CommitBodyV0::AddLink(_) => true,
991                CommitBodyV0::RemoveLink(_) => true,
992                CommitBodyV0::AddSignerCap(_) => true,
993                CommitBodyV0::RemoveSignerCap(_) => true,
994                CommitBodyV0::WalletUpdate(_) => true,
995                CommitBodyV0::StoreUpdate(_) => true,
996                _ => false,
997            },
998        }
999    }
1000
1001    pub fn not_allowed_on_individual_private_site(&self) -> bool {
1002        match self {
1003            Self::V0(v0) => match v0 {
1004                CommitBodyV0::SyncTransaction(_) => true,
1005                CommitBodyV0::AddMember(_) => true,
1006                CommitBodyV0::RemoveMember(_) => true,
1007                CommitBodyV0::AddPermission(_) => true,
1008                CommitBodyV0::RemovePermission(_) => true,
1009                _ => false,
1010            },
1011        }
1012    }
1013
1014    pub fn total_order_required(&self) -> bool {
1015        match self {
1016            Self::V0(v0) => match v0 {
1017                CommitBodyV0::RootBranch(_) => true,
1018                CommitBodyV0::Branch(_) => true,
1019                CommitBodyV0::UpdateRootBranch(_) => true,
1020                CommitBodyV0::UpdateBranch(_) => true,
1021                CommitBodyV0::AddBranch(AddBranch::V0(AddBranchV0 {
1022                    branch_type: BranchType::Transactional,
1023                    ..
1024                })) => false,
1025                CommitBodyV0::AddBranch(AddBranch::V0(AddBranchV0 { branch_type: _, .. })) => true,
1026                CommitBodyV0::RemoveBranch(_) => true,
1027                //CommitBodyV0::AddMember(_) => true,
1028                CommitBodyV0::RemoveMember(_) => true,
1029                CommitBodyV0::RemovePermission(_) => true,
1030                //CommitBodyV0::Quorum(_) => true,
1031                CommitBodyV0::Compact(_) => true,
1032                CommitBodyV0::SyncTransaction(_) => true, // check QuorumType::TotalOrder in CommitContent
1033                CommitBodyV0::RootCapRefresh(_) => true,
1034                CommitBodyV0::BranchCapRefresh(_) => true,
1035                _ => false,
1036            },
1037        }
1038    }
1039    pub fn required_permission(&self) -> HashSet<PermissionV0> {
1040        let res: Vec<PermissionV0>;
1041        res = match self {
1042            Self::V0(v0) => match v0 {
1043                CommitBodyV0::Repository(_) => vec![PermissionV0::Create],
1044                CommitBodyV0::RootBranch(_) => vec![PermissionV0::Create],
1045                CommitBodyV0::UpdateRootBranch(_) => vec![
1046                    PermissionV0::ChangeQuorum,
1047                    PermissionV0::RefreshWriteCap,
1048                    PermissionV0::RefreshReadCap,
1049                    PermissionV0::RefreshOverlay,
1050                ],
1051                CommitBodyV0::AddMember(_) => {
1052                    vec![PermissionV0::Create, PermissionV0::AddReadMember]
1053                }
1054                CommitBodyV0::RemoveMember(_) => vec![PermissionV0::RemoveMember],
1055                CommitBodyV0::AddPermission(addp) => {
1056                    let mut perms = vec![PermissionV0::Create];
1057                    if addp.permission_v0().is_delegated_by_admin() {
1058                        perms.push(PermissionV0::Admin);
1059                    }
1060                    if addp.permission_v0().is_write_permission() {
1061                        perms.push(PermissionV0::AddWritePermission);
1062                    }
1063                    perms
1064                }
1065                CommitBodyV0::RemovePermission(remp) => {
1066                    let mut perms = vec![];
1067                    if remp.permission_v0().is_delegated_by_admin() {
1068                        perms.push(PermissionV0::Admin);
1069                    }
1070                    if remp.permission_v0().is_write_permission() {
1071                        perms.push(PermissionV0::RemoveWritePermission);
1072                    }
1073                    perms
1074                }
1075                CommitBodyV0::AddBranch(_) => vec![
1076                    PermissionV0::Create,
1077                    PermissionV0::AddBranch,
1078                    PermissionV0::RefreshReadCap,
1079                    PermissionV0::RefreshWriteCap,
1080                    PermissionV0::RefreshOverlay,
1081                    PermissionV0::ChangeMainBranch,
1082                ],
1083                CommitBodyV0::RemoveBranch(_) => vec![PermissionV0::RemoveBranch],
1084                CommitBodyV0::UpdateBranch(_) => {
1085                    vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1086                }
1087                CommitBodyV0::AddName(_) => vec![PermissionV0::AddBranch, PermissionV0::ChangeName],
1088                CommitBodyV0::RemoveName(_) => {
1089                    vec![PermissionV0::ChangeName, PermissionV0::RemoveBranch]
1090                }
1091                CommitBodyV0::Branch(_) => vec![PermissionV0::Create, PermissionV0::AddBranch],
1092                CommitBodyV0::Snapshot(_) => vec![PermissionV0::WriteAsync],
1093                CommitBodyV0::Compact(_) => vec![PermissionV0::Compact],
1094                CommitBodyV0::AsyncTransaction(_) => vec![PermissionV0::WriteAsync],
1095                CommitBodyV0::AddFile(_) => vec![PermissionV0::WriteAsync, PermissionV0::WriteSync],
1096                CommitBodyV0::RemoveFile(_) => {
1097                    vec![PermissionV0::WriteAsync, PermissionV0::WriteSync]
1098                }
1099                CommitBodyV0::SyncTransaction(_) => vec![PermissionV0::WriteSync],
1100                CommitBodyV0::AsyncSignature(_) => vec![PermissionV0::WriteAsync],
1101                CommitBodyV0::SyncSignature(_) => vec![
1102                    PermissionV0::WriteSync,
1103                    PermissionV0::ChangeQuorum,
1104                    PermissionV0::RefreshWriteCap,
1105                    PermissionV0::RefreshReadCap,
1106                    PermissionV0::RefreshOverlay,
1107                    PermissionV0::ChangeMainBranch,
1108                    PermissionV0::AddBranch,
1109                    PermissionV0::RemoveBranch,
1110                    PermissionV0::AddReadMember,
1111                    PermissionV0::RemoveMember,
1112                    PermissionV0::RemoveWritePermission,
1113                    PermissionV0::Compact,
1114                ],
1115                CommitBodyV0::RootCapRefresh(_) => {
1116                    vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1117                }
1118                CommitBodyV0::BranchCapRefresh(_) => {
1119                    vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1120                }
1121                CommitBodyV0::CapRefreshed(_) => {
1122                    vec![PermissionV0::RefreshReadCap, PermissionV0::RefreshWriteCap]
1123                }
1124                CommitBodyV0::Delete(_) => vec![],
1125                CommitBodyV0::AddRepo(_)
1126                | CommitBodyV0::RemoveRepo(_)
1127                | CommitBodyV0::AddLink(_)
1128                | CommitBodyV0::RemoveLink(_)
1129                | CommitBodyV0::AddSignerCap(_)
1130                | CommitBodyV0::RemoveSignerCap(_)
1131                | CommitBodyV0::WalletUpdate(_)
1132                | CommitBodyV0::StoreUpdate(_) => vec![],
1133            },
1134        };
1135        HashSet::from_iter(res.iter().cloned())
1136    }
1137}
1138
1139impl fmt::Display for CommitHeader {
1140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1141        match self {
1142            CommitHeader::V0(v0) => {
1143                writeln!(
1144                    f,
1145                    "v0 - compact:{} id:{}",
1146                    v0.compact,
1147                    v0.id.map_or("None".to_string(), |i| format!("{}", i))
1148                )?;
1149                writeln!(f, "====  acks  : {}", v0.acks.len())?;
1150                for ack in &v0.acks {
1151                    writeln!(f, "============== {}", ack)?;
1152                }
1153                writeln!(f, "==== nacks  : {}", v0.nacks.len())?;
1154                for nack in &v0.nacks {
1155                    writeln!(f, "============== {}", nack)?;
1156                }
1157                writeln!(f, "====  deps  : {}", v0.deps.len())?;
1158                for dep in &v0.deps {
1159                    writeln!(f, "============== {}", dep)?;
1160                }
1161                writeln!(f, "==== ndeps  : {}", v0.ndeps.len())?;
1162                for ndep in &v0.ndeps {
1163                    writeln!(f, "============== {}", ndep)?;
1164                }
1165                writeln!(f, "====  files : {}", v0.files.len())?;
1166                for file in &v0.files {
1167                    writeln!(f, "============== {}", file)?;
1168                }
1169                writeln!(f, "==== nfiles : {}", v0.nfiles.len())?;
1170                for nfile in &v0.nfiles {
1171                    writeln!(f, "============== {}", nfile)?;
1172                }
1173                Ok(())
1174            }
1175        }
1176    }
1177}
1178
1179impl CommitHeader {
1180    pub fn is_root(&self) -> bool {
1181        match self {
1182            CommitHeader::V0(v0) => v0.is_root(),
1183        }
1184    }
1185    pub fn deps(&self) -> Vec<ObjectId> {
1186        match self {
1187            CommitHeader::V0(v0) => v0.deps.clone(),
1188        }
1189    }
1190    pub fn acks(&self) -> Vec<ObjectId> {
1191        match self {
1192            CommitHeader::V0(v0) => v0.acks.clone(),
1193        }
1194    }
1195    pub fn files(&self) -> &Vec<ObjectId> {
1196        match self {
1197            CommitHeader::V0(v0) => &v0.files,
1198        }
1199    }
1200    pub fn acks_and_nacks(&self) -> Vec<ObjectId> {
1201        match self {
1202            CommitHeader::V0(v0) => {
1203                let mut res = v0.acks.clone();
1204                res.extend_from_slice(&v0.nacks);
1205                res
1206            }
1207        }
1208    }
1209    pub fn id(&self) -> &Option<ObjectId> {
1210        match self {
1211            CommitHeader::V0(v0) => &v0.id,
1212        }
1213    }
1214
1215    pub fn set_id(&mut self, id: Digest) {
1216        match self {
1217            CommitHeader::V0(v0) => v0.id = Some(id),
1218        }
1219    }
1220
1221    pub fn set_compact(&mut self) {
1222        match self {
1223            CommitHeader::V0(v0) => v0.set_compact(),
1224        }
1225    }
1226
1227    pub fn verify(&self) -> bool {
1228        match self {
1229            CommitHeader::V0(v0) => v0.verify(),
1230        }
1231    }
1232
1233    pub fn new_with(
1234        deps: Vec<ObjectRef>,
1235        ndeps: Vec<ObjectRef>,
1236        acks: Vec<ObjectRef>,
1237        nacks: Vec<ObjectRef>,
1238        files: Vec<ObjectRef>,
1239        nfiles: Vec<ObjectRef>,
1240    ) -> (Option<Self>, Option<CommitHeaderKeys>) {
1241        let res = CommitHeaderV0::new_with(deps, ndeps, acks, nacks, files, nfiles);
1242        (
1243            res.0.map(|h| CommitHeader::V0(h)),
1244            res.1.map(|h| CommitHeaderKeys::V0(h)),
1245        )
1246    }
1247
1248    #[cfg(test)]
1249    pub fn new_invalid() -> (Option<Self>, Option<CommitHeaderKeys>) {
1250        let res = CommitHeaderV0::new_invalid();
1251        (
1252            res.0.map(|h| CommitHeader::V0(h)),
1253            res.1.map(|h| CommitHeaderKeys::V0(h)),
1254        )
1255    }
1256
1257    #[cfg(test)]
1258    pub fn new_with_deps(deps: Vec<ObjectId>) -> Option<Self> {
1259        CommitHeaderV0::new_with_deps(deps).map(|ch| CommitHeader::V0(ch))
1260    }
1261
1262    #[cfg(test)]
1263    pub fn new_with_deps_and_acks(deps: Vec<ObjectId>, acks: Vec<ObjectId>) -> Option<Self> {
1264        CommitHeaderV0::new_with_deps_and_acks(deps, acks).map(|ch| CommitHeader::V0(ch))
1265    }
1266
1267    #[cfg(test)]
1268    pub fn new_with_acks(acks: Vec<ObjectId>) -> Option<Self> {
1269        CommitHeaderV0::new_with_acks(acks).map(|ch| CommitHeader::V0(ch))
1270    }
1271}
1272
1273impl CommitHeaderV0 {
1274    #[allow(dead_code)]
1275    fn new_empty() -> Self {
1276        Self {
1277            id: None,
1278            compact: false,
1279            deps: vec![],
1280            ndeps: vec![],
1281            acks: vec![],
1282            nacks: vec![],
1283            files: vec![],
1284            nfiles: vec![],
1285        }
1286    }
1287
1288    #[cfg(test)]
1289    fn new_invalid() -> (Option<Self>, Option<CommitHeaderKeysV0>) {
1290        let ideps: Vec<ObjectId> = vec![ObjectId::dummy()];
1291        let kdeps: Vec<ObjectKey> = vec![ObjectKey::dummy()];
1292
1293        let res = Self {
1294            id: None,
1295            compact: false,
1296            deps: ideps.clone(),
1297            ndeps: ideps,
1298            acks: vec![],
1299            nacks: vec![],
1300            files: vec![],
1301            nfiles: vec![],
1302        };
1303        (
1304            Some(res),
1305            Some(CommitHeaderKeysV0 {
1306                deps: kdeps,
1307                acks: vec![],
1308                nacks: vec![],
1309                files: vec![],
1310            }),
1311        )
1312    }
1313
1314    pub fn verify(&self) -> bool {
1315        if !self.deps.is_empty() && !self.ndeps.is_empty() {
1316            for ndep in self.ndeps.iter() {
1317                if self.deps.contains(ndep) {
1318                    return false;
1319                }
1320            }
1321        }
1322        if !self.acks.is_empty() && !self.nacks.is_empty() {
1323            for nack in self.nacks.iter() {
1324                if self.acks.contains(nack) {
1325                    return false;
1326                }
1327            }
1328        }
1329        if !self.files.is_empty() && !self.nfiles.is_empty() {
1330            for nref in self.nfiles.iter() {
1331                if self.files.contains(nref) {
1332                    return false;
1333                }
1334            }
1335        }
1336        true
1337    }
1338
1339    pub fn set_compact(&mut self) {
1340        self.compact = true;
1341    }
1342
1343    pub fn new_with(
1344        deps: Vec<ObjectRef>,
1345        ndeps: Vec<ObjectRef>,
1346        acks: Vec<ObjectRef>,
1347        nacks: Vec<ObjectRef>,
1348        files: Vec<ObjectRef>,
1349        nfiles: Vec<ObjectRef>,
1350    ) -> (Option<Self>, Option<CommitHeaderKeysV0>) {
1351        if deps.is_empty()
1352            && ndeps.is_empty()
1353            && acks.is_empty()
1354            && nacks.is_empty()
1355            && files.is_empty()
1356            && nfiles.is_empty()
1357        {
1358            (None, None)
1359        } else {
1360            let mut ideps: Vec<ObjectId> = vec![];
1361            let mut indeps: Vec<ObjectId> = vec![];
1362            let mut iacks: Vec<ObjectId> = vec![];
1363            let mut inacks: Vec<ObjectId> = vec![];
1364            let mut ifiles: Vec<ObjectId> = vec![];
1365            let mut infiles: Vec<ObjectId> = vec![];
1366
1367            let mut kdeps: Vec<ObjectKey> = vec![];
1368            let mut kacks: Vec<ObjectKey> = vec![];
1369            let mut knacks: Vec<ObjectKey> = vec![];
1370            for d in deps {
1371                ideps.push(d.id);
1372                kdeps.push(d.key);
1373            }
1374            for d in ndeps {
1375                indeps.push(d.id);
1376            }
1377            for d in acks {
1378                iacks.push(d.id);
1379                kacks.push(d.key);
1380            }
1381            for d in nacks {
1382                inacks.push(d.id);
1383                knacks.push(d.key);
1384            }
1385            for d in files.clone() {
1386                ifiles.push(d.id);
1387            }
1388            for d in nfiles {
1389                infiles.push(d.id);
1390            }
1391            let res = Self {
1392                id: None,
1393                compact: false,
1394                deps: ideps,
1395                ndeps: indeps,
1396                acks: iacks,
1397                nacks: inacks,
1398                files: ifiles,
1399                nfiles: infiles,
1400            };
1401            if !res.verify() {
1402                panic!("cannot create a header with conflicting references");
1403            }
1404            (
1405                Some(res),
1406                Some(CommitHeaderKeysV0 {
1407                    deps: kdeps,
1408                    acks: kacks,
1409                    nacks: knacks,
1410                    files,
1411                }),
1412            )
1413        }
1414    }
1415
1416    #[cfg(test)]
1417    pub fn new_with_deps(deps: Vec<ObjectId>) -> Option<Self> {
1418        assert!(!deps.is_empty());
1419        let mut n = Self::new_empty();
1420        n.deps = deps;
1421        Some(n)
1422    }
1423
1424    #[cfg(test)]
1425    pub fn new_with_deps_and_acks(deps: Vec<ObjectId>, acks: Vec<ObjectId>) -> Option<Self> {
1426        if deps.is_empty() && acks.is_empty() {
1427            return None;
1428        }
1429        //assert!(!deps.is_empty() || !acks.is_empty());
1430        let mut n = Self::new_empty();
1431        n.deps = deps;
1432        n.acks = acks;
1433        Some(n)
1434    }
1435
1436    #[cfg(test)]
1437    pub fn new_with_acks(acks: Vec<ObjectId>) -> Option<Self> {
1438        assert!(!acks.is_empty());
1439        let mut n = Self::new_empty();
1440        n.acks = acks;
1441        Some(n)
1442    }
1443
1444    /// we do not check the deps because in a forked branch, they point to previous branch heads.
1445    pub fn is_root(&self) -> bool {
1446        //self.deps.is_empty()
1447        //    && self.ndeps.is_empty()
1448        self.acks.is_empty() && self.nacks.is_empty()
1449    }
1450}
1451
1452impl fmt::Display for Commit {
1453    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1454        match self {
1455            Self::V0(v0) => {
1456                writeln!(f, "====== Commit V0 ======")?;
1457                if v0.id.is_some() {
1458                    writeln!(f, "== ID:    {}", v0.id.as_ref().unwrap())?;
1459                }
1460                if v0.key.is_some() {
1461                    writeln!(f, "== Key:   {}", v0.key.as_ref().unwrap())?;
1462                }
1463                if v0.header.is_some() {
1464                    write!(f, "== Header:   {}", v0.header.as_ref().unwrap())?;
1465                }
1466                writeln!(f, "== Sig:   {}", v0.sig)?;
1467                write!(f, "{}", v0.content)?;
1468                if v0.body.get().is_some() {
1469                    write!(f, "== Body:   {}", v0.body.get().unwrap())?;
1470                }
1471            }
1472        }
1473        Ok(())
1474    }
1475}
1476
1477impl fmt::Display for CommitBody {
1478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479        match self {
1480            Self::V0(v0) => {
1481                write!(f, "V0 ")?;
1482                match v0 {
1483                    //
1484                    // for root branch:
1485                    //
1486                    CommitBodyV0::Repository(b) => write!(f, "Repository {}", b),
1487                    CommitBodyV0::RootBranch(b) => write!(f, "RootBranch {}", b),
1488
1489                    CommitBodyV0::UpdateRootBranch(b) => write!(f, "UpdateRootBranch {}", b), // total order enforced with total_order_quorum
1490                    // CommitBodyV0::AddMember(b) => write!(f, "AddMember {}", b), // total order enforced with total_order_quorum
1491                    // CommitBodyV0::RemoveMember(b) => write!(f, "RemoveMember {}", b), // total order enforced with total_order_quorum
1492                    // CommitBodyV0::AddPermission(b) => write!(f, "AddPermission {}", b),
1493                    // CommitBodyV0::RemovePermission(b) => {
1494                    //     write!(f, "RemovePermission {}", b)
1495                    // }
1496                    CommitBodyV0::AddBranch(b) => write!(f, "AddBranch {}", b),
1497                    // CommitBodyV0::RemoveBranch(b) => write!(f, "RemoveBranch {}", b),
1498                    // CommitBodyV0::AddName(b) => write!(f, "AddName {}", b),
1499                    // CommitBodyV0::RemoveName(b) => write!(f, "RemoveName {}", b),
1500                    // TODO? Quorum(Quorum) => write!(f, "RootBranch {}", b), // changes the quorum without changing the RootBranch
1501
1502                    //
1503                    // For transactional branches:
1504                    //
1505                    CommitBodyV0::Branch(b) => write!(f, "Branch {}", b), // singleton and should be first in branch
1506                    // CommitBodyV0::UpdateBranch(b) => write!(f, "UpdateBranch {}", b), // total order enforced with total_order_quorum
1507                    CommitBodyV0::Snapshot(b) => write!(f, "Snapshot {}", b), // a soft snapshot
1508                    // CommitBodyV0::AsyncTransaction(b) => write!(f, "AsyncTransaction {}", b), // partial_order
1509                    // CommitBodyV0::SyncTransaction(b) => write!(f, "SyncTransaction {}", b), // total_order
1510                    CommitBodyV0::AddFile(b) => write!(f, "AddFile {}", b),
1511                    // CommitBodyV0::RemoveFile(b) => write!(f, "RemoveFile {}", b),
1512                    // CommitBodyV0::Compact(b) => write!(f, "Compact {}", b), // a hard snapshot. total order enforced with total_order_quorum
1513                    //Merge(Merge) => write!(f, "RootBranch {}", b),
1514                    //Revert(Revert) => write!(f, "RootBranch {}", b), // only possible on partial order commit
1515                    CommitBodyV0::AsyncSignature(b) => write!(f, "AsyncSignature {}", b),
1516
1517                    //
1518                    // For both
1519                    //
1520                    // CommitBodyV0::RootCapRefresh(b) => write!(f, "RootCapRefresh {}", b),
1521                    // CommitBodyV0::BranchCapRefresh(b) => {
1522                    //     write!(f, "BranchCapRefresh {}", b)
1523                    // }
1524                    CommitBodyV0::SyncSignature(b) => write!(f, "SyncSignature {}", b),
1525                    //CommitBodyV0::AddRepo(b) => write!(f, "AddRepo {}", b),
1526                    //CommitBodyV0::RemoveRepo(b) => write!(f, "RemoveRepo {}", b),
1527                    CommitBodyV0::AddSignerCap(b) => write!(f, "AddSignerCap {}", b),
1528                    CommitBodyV0::StoreUpdate(b) => write!(f, "StoreUpdate {}", b),
1529                    /*    AddLink(AddLink),
1530                    RemoveLink(RemoveLink),
1531                    AddSignerCap(AddSignerCap),
1532                    RemoveSignerCap(RemoveSignerCap),
1533                    WalletUpdate(WalletUpdate),
1534                    StoreUpdate(StoreUpdate), */
1535                    _ => unimplemented!(),
1536                }
1537            }
1538        }
1539    }
1540}
1541
1542impl fmt::Display for CommitContent {
1543    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1544        match self {
1545            Self::V0(v0) => {
1546                writeln!(f, "=== CommitContent V0 ===")?;
1547                writeln!(f, "====== author:   {}", v0.author)?;
1548                //writeln!(f, "====== seq:      {}", v0.seq)?;
1549                writeln!(f, "====== BranchID: {}", v0.branch)?;
1550                writeln!(f, "====== quorum:   {:?}", v0.quorum)?;
1551                writeln!(f, "====== Ref body: {}", v0.body)?;
1552                if v0.header_keys.is_none() {
1553                    writeln!(f, "====== header keys: None")?;
1554                } else {
1555                    write!(f, "{}", v0.header_keys.as_ref().unwrap())?;
1556                }
1557                writeln!(f, "====== Perms commits: {}", v0.perms.len())?;
1558                let mut i = 0;
1559                for block in &v0.perms {
1560                    writeln!(f, "========== {:03}: {}", i, block)?;
1561                    i += 1;
1562                }
1563            }
1564        }
1565        Ok(())
1566    }
1567}
1568
1569impl fmt::Display for CommitHeaderKeys {
1570    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1571        match self {
1572            Self::V0(v0) => {
1573                writeln!(f, "=== CommitHeaderKeys V0 ===")?;
1574                writeln!(f, "====   acks : {}", v0.acks.len())?;
1575                for ack in &v0.acks {
1576                    writeln!(f, "============== {}", ack)?;
1577                }
1578                writeln!(f, "====  nacks : {}", v0.nacks.len())?;
1579                for nack in &v0.nacks {
1580                    writeln!(f, "============== {}", nack)?;
1581                }
1582                writeln!(f, "====   deps : {}", v0.deps.len())?;
1583                for dep in &v0.deps {
1584                    writeln!(f, "============== {}", dep)?;
1585                }
1586                writeln!(f, "====   files : {}", v0.files.len())?;
1587                for file in &v0.files {
1588                    writeln!(f, "============== {}", file)?;
1589                }
1590            }
1591        }
1592        Ok(())
1593    }
1594}
1595
1596#[cfg(test)]
1597mod test {
1598    use crate::commit::*;
1599    #[allow(unused_imports)]
1600    use crate::log::*;
1601
1602    fn test_commit_header_ref_content_fits(
1603        obj_refs: Vec<BlockRef>,
1604        metadata_size: usize,
1605        expect_blocks_len: usize,
1606    ) {
1607        let (priv_key, pub_key) = generate_keypair();
1608        let obj_ref = ObjectRef::dummy();
1609
1610        let branch = pub_key;
1611        let deps = obj_refs.clone();
1612        let acks = obj_refs.clone();
1613        let files = obj_refs.clone();
1614        let body_ref = obj_ref.clone();
1615        let overlay = OverlayId::dummy();
1616
1617        let metadata = vec![66; metadata_size];
1618
1619        let mut commit = Commit::new(
1620            &priv_key,
1621            &pub_key,
1622            overlay,
1623            branch,
1624            QuorumType::NoSigning,
1625            deps,
1626            vec![],
1627            acks.clone(),
1628            vec![],
1629            files,
1630            vec![],
1631            metadata,
1632            body_ref,
1633        )
1634        .unwrap();
1635
1636        log_debug!("{}", commit);
1637
1638        let max_object_size = 0;
1639
1640        let store = Store::dummy_public_v0();
1641
1642        let commit_ref = commit.save(max_object_size, &store).expect("save commit");
1643
1644        let commit_object =
1645            Object::load(commit_ref.id.clone(), Some(commit_ref.key.clone()), &store)
1646                .expect("load object from storage");
1647
1648        assert_eq!(
1649            commit_object.acks(),
1650            acks.iter().map(|a| a.id).collect::<Vec<ObjectId>>()
1651        );
1652
1653        log_debug!("{}", commit_object);
1654
1655        // log_debug!("blocks:     {}", commit_object.blocks_len());
1656        // log_debug!("header blocks:     {}", commit_object.header_blocks_len());
1657        // log_debug!("object size:     {}", commit_object.size());
1658
1659        assert_eq!(commit_object.all_blocks_len(), expect_blocks_len);
1660
1661        let commit = Commit::load(commit_ref, &store, false).expect("load commit from storage");
1662
1663        log_debug!("{}", commit);
1664    }
1665
1666    #[test]
1667    pub fn test_commit_header_ref_content_fits_or_not() {
1668        let obj_ref = ObjectRef::dummy();
1669        let obj_refs2 = vec![obj_ref.clone(), obj_ref.clone()];
1670        let obj_refs = vec![obj_ref.clone()];
1671        // with 1 refs in header
1672        test_commit_header_ref_content_fits(obj_refs.clone(), 3592, 1); // block 4090
1673        test_commit_header_ref_content_fits(obj_refs.clone(), 3593, 2); //block 4012 header 117 total: 4129
1674        test_commit_header_ref_content_fits(obj_refs.clone(), 3741, 2); //block 4094 block 219 total: 4313
1675        test_commit_header_ref_content_fits(obj_refs.clone(), 3742, 3); // block 4094 block 9 block 285
1676
1677        // with 2 refs in header
1678        test_commit_header_ref_content_fits(obj_refs2.clone(), 3360, 1);
1679        test_commit_header_ref_content_fits(obj_refs2.clone(), 3361, 2);
1680        test_commit_header_ref_content_fits(obj_refs2.clone(), 3609, 2);
1681        test_commit_header_ref_content_fits(obj_refs2.clone(), 3610, 3);
1682    }
1683
1684    #[test]
1685    pub fn test_load_commit_fails_on_non_commit_object() {
1686        let file = SmallFile::V0(SmallFileV0 {
1687            content_type: "file/test".into(),
1688            metadata: Vec::from("some meta data here"),
1689            content: [(0..255).collect::<Vec<u8>>().as_slice(); 320].concat(),
1690        });
1691        let content = ObjectContent::V0(ObjectContentV0::SmallFile(file));
1692
1693        let max_object_size = 0;
1694
1695        let store = Store::dummy_public_v0();
1696
1697        let obj = Object::new(content.clone(), None, max_object_size, &store);
1698
1699        _ = obj.save(&store).expect("save object");
1700
1701        let commit = Commit::load(obj.reference().unwrap(), &store, false);
1702
1703        assert_eq!(commit, Err(CommitLoadError::NotACommit));
1704    }
1705
1706    #[test]
1707    pub fn test_load_commit_with_body() {
1708        let (priv_key, pub_key) = generate_keypair();
1709        let obj_ref = ObjectRef::dummy();
1710
1711        let branch = pub_key;
1712        let obj_refs = vec![obj_ref.clone()];
1713        let deps = obj_refs.clone();
1714        let acks = obj_refs.clone();
1715        let files = obj_refs.clone();
1716
1717        let metadata = Vec::from("some metadata");
1718
1719        let body = CommitBody::V0(CommitBodyV0::Repository(Repository::new(&branch)));
1720
1721        let max_object_size = 0;
1722
1723        let store = Store::dummy_public_v0();
1724
1725        let mut commit = Commit::new_with_body_and_save(
1726            &priv_key,
1727            &pub_key,
1728            branch,
1729            QuorumType::NoSigning,
1730            deps,
1731            vec![],
1732            acks.clone(),
1733            vec![],
1734            files,
1735            vec![],
1736            metadata,
1737            body,
1738            max_object_size,
1739            &store,
1740        )
1741        .expect("commit::new_with_body_and_save");
1742
1743        log_debug!("{}", commit);
1744
1745        commit.empty_blocks();
1746
1747        let commit2 = Commit::load(commit.reference().unwrap(), &store, true)
1748            .expect("load commit with body after save");
1749
1750        log_debug!("{}", commit2);
1751
1752        assert_eq!(commit, commit2);
1753    }
1754
1755    #[test]
1756    pub fn test_commit_load_body_fails() {
1757        let (priv_key, pub_key) = generate_keypair();
1758        let obj_ref = ObjectRef::dummy();
1759        let obj_refs = vec![obj_ref.clone()];
1760        let branch = pub_key;
1761        let deps = obj_refs.clone();
1762        let acks = obj_refs.clone();
1763        let files = obj_refs.clone();
1764        let metadata = vec![1, 2, 3];
1765        let body_ref = obj_ref.clone();
1766        let store = Store::dummy_public_v0();
1767
1768        let commit = Commit::new(
1769            &priv_key,
1770            &pub_key,
1771            store.overlay_id,
1772            branch,
1773            QuorumType::NoSigning,
1774            deps,
1775            vec![],
1776            acks,
1777            vec![],
1778            files,
1779            vec![],
1780            metadata,
1781            body_ref,
1782        )
1783        .unwrap();
1784        log_debug!("{}", commit);
1785
1786        let repo = Repo::new_with_member(&pub_key, &pub_key, &[PermissionV0::Create], store);
1787
1788        // match commit.load_body(repo.store.unwrap()) {
1789        //     Ok(_b) => panic!("Body should not exist"),
1790        //     Err(CommitLoadError::MissingBlocks(missing)) => {
1791        //         assert_eq!(missing.len(), 1);
1792        //     }
1793        //     Err(e) => panic!("Commit load error: {:?}", e),
1794        // }
1795
1796        commit.verify_sig(&repo).expect("verify signature");
1797        match commit.verify_perm(&repo) {
1798            Ok(_) => panic!("Commit should not be Ok"),
1799            Err(NgError::CommitLoadError(CommitLoadError::MissingBlocks(missing))) => {
1800                assert_eq!(missing.len(), 1);
1801            }
1802            Err(e) => panic!("Commit verify perm error: {:?}", e),
1803        }
1804
1805        // match commit.verify_full_object_refs_of_branch_at_commit(repo.store.unwrap()) {
1806        //     Ok(_) => panic!("Commit should not be Ok"),
1807        //     Err(CommitLoadError::MissingBlocks(missing)) => {
1808        //         assert_eq!(missing.len(), 1);
1809        //     }
1810        //     Err(e) => panic!("Commit verify error: {:?}", e),
1811        // }
1812
1813        match commit.verify(&repo) {
1814            Ok(_) => panic!("Commit should not be Ok"),
1815            Err(NgError::CommitLoadError(CommitLoadError::MissingBlocks(missing))) => {
1816                assert_eq!(missing.len(), 1);
1817            }
1818            Err(e) => panic!("Commit verify error: {:?}", e),
1819        }
1820    }
1821
1822    #[test]
1823    pub fn test_load_commit_with_body_verify_perms() {
1824        let (priv_key, pub_key) = generate_keypair();
1825
1826        let branch = pub_key;
1827
1828        let metadata = Vec::from("some metadata");
1829
1830        let body = CommitBody::V0(CommitBodyV0::Repository(Repository::new(&branch)));
1831
1832        let max_object_size = 0;
1833
1834        let store = Store::dummy_public_v0();
1835
1836        let commit = Commit::new_with_body_and_save(
1837            &priv_key,
1838            &pub_key,
1839            branch,
1840            QuorumType::NoSigning,
1841            vec![],
1842            vec![],
1843            vec![], //acks.clone(),
1844            vec![],
1845            vec![],
1846            vec![],
1847            metadata,
1848            body,
1849            max_object_size,
1850            &store,
1851        )
1852        .expect("commit::new_with_body_and_save");
1853
1854        log_debug!("{}", commit);
1855
1856        let repo = Repo::new_with_member(&pub_key, &pub_key, &[PermissionV0::Create], store);
1857
1858        commit.load_body(&repo.store).expect("load body");
1859
1860        commit.verify_sig(&repo).expect("verify signature");
1861        commit.verify_perm(&repo).expect("verify perms");
1862        commit
1863            .verify_perm_creation(None)
1864            .expect("verify_perm_creation");
1865
1866        commit
1867            .verify_full_object_refs_of_branch_at_commit(&repo.store)
1868            .expect("verify is at root of branch and singleton");
1869
1870        commit.verify(&repo).expect("verify");
1871    }
1872
1873    #[test]
1874    pub fn test_load_commit_with_invalid_header() {
1875        let (priv_key, pub_key) = generate_keypair();
1876        let obj_ref = ObjectRef::dummy();
1877
1878        let branch = pub_key;
1879        let metadata = Vec::from("some metadata");
1880
1881        //let max_object_size = 0;
1882        //let store = Store::dummy_public_v0();
1883
1884        let commit = Commit::V0(
1885            CommitV0::new_with_invalid_header(
1886                &priv_key,
1887                &pub_key,
1888                branch,
1889                QuorumType::NoSigning,
1890                metadata,
1891                obj_ref,
1892            )
1893            .expect("commit::new_with_invalid_header"),
1894        );
1895
1896        log_debug!("{}", commit);
1897
1898        let store = Store::dummy_public_v0();
1899        let repo = Repo::new_with_perms(&[PermissionV0::Create], store);
1900
1901        assert_eq!(
1902            commit.verify(&repo),
1903            Err(NgError::CommitVerifyError(CommitVerifyError::InvalidHeader))
1904        );
1905    }
1906}